entry_osx.mm 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843
  1. /*
  2. * Copyright 2011-2025 Branimir Karadzic. All rights reserved.
  3. * License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE
  4. */
  5. #include "entry_p.h"
  6. #if ENTRY_CONFIG_USE_NATIVE && BX_PLATFORM_OSX
  7. #import <Cocoa/Cocoa.h>
  8. #include <bgfx/platform.h>
  9. #include <bx/uint32_t.h>
  10. #include <bx/thread.h>
  11. #include <bx/os.h>
  12. #include <bx/handlealloc.h>
  13. @interface AppDelegate : NSObject<NSApplicationDelegate>
  14. {
  15. bool terminated;
  16. }
  17. + (AppDelegate *)sharedDelegate;
  18. - (id)init;
  19. - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
  20. - (bool)applicationHasTerminated;
  21. @end
  22. @interface Window : NSObject<NSWindowDelegate>
  23. {
  24. }
  25. + (Window*)sharedDelegate;
  26. - (id)init;
  27. - (void)windowCreated:(NSWindow*)window;
  28. - (void)windowWillClose:(NSNotification*)notification;
  29. - (BOOL)windowShouldClose:(NSWindow*)window;
  30. - (void)windowDidResize:(NSNotification*)notification;
  31. - (void)windowDidBecomeKey:(NSNotification *)notification;
  32. - (void)windowDidResignKey:(NSNotification *)notification;
  33. @end
  34. namespace entry
  35. {
  36. static uint8_t s_translateKey[256];
  37. struct MainThreadEntry
  38. {
  39. int m_argc;
  40. const char* const* m_argv;
  41. static int32_t threadFunc(bx::Thread* _thread, void* _userData)
  42. {
  43. BX_UNUSED(_thread);
  44. CFBundleRef mainBundle = CFBundleGetMainBundle();
  45. if (mainBundle != nil)
  46. {
  47. CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
  48. if (resourcesURL != nil)
  49. {
  50. char path[PATH_MAX];
  51. if (CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8*)path, PATH_MAX) )
  52. {
  53. // This breaks console apps, but it's not needed on windowed.
  54. //chdir(path);
  55. }
  56. CFRelease(resourcesURL);
  57. }
  58. }
  59. MainThreadEntry* self = (MainThreadEntry*)_userData;
  60. uint32_t result = main(self->m_argc, self->m_argv);
  61. [NSApp terminate:nil];
  62. return result;
  63. }
  64. };
  65. struct Context
  66. {
  67. Context()
  68. : m_scrollf(0.0f)
  69. , m_mx(0)
  70. , m_my(0)
  71. , m_scroll(0)
  72. , m_style(0)
  73. , m_exit(false)
  74. , m_mouseLock(NULL)
  75. {
  76. s_translateKey[27] = Key::Esc;
  77. s_translateKey[uint8_t('\r')] = Key::Return;
  78. s_translateKey[uint8_t('\t')] = Key::Tab;
  79. s_translateKey[127] = Key::Backspace;
  80. s_translateKey[uint8_t(' ')] = Key::Space;
  81. s_translateKey[uint8_t('+')] =
  82. s_translateKey[uint8_t('=')] = Key::Plus;
  83. s_translateKey[uint8_t('_')] =
  84. s_translateKey[uint8_t('-')] = Key::Minus;
  85. s_translateKey[uint8_t('~')] =
  86. s_translateKey[uint8_t('`')] = Key::Tilde;
  87. s_translateKey[uint8_t(':')] =
  88. s_translateKey[uint8_t(';')] = Key::Semicolon;
  89. s_translateKey[uint8_t('"')] =
  90. s_translateKey[uint8_t('\'')] = Key::Quote;
  91. s_translateKey[uint8_t('{')] =
  92. s_translateKey[uint8_t('[')] = Key::LeftBracket;
  93. s_translateKey[uint8_t('}')] =
  94. s_translateKey[uint8_t(']')] = Key::RightBracket;
  95. s_translateKey[uint8_t('<')] =
  96. s_translateKey[uint8_t(',')] = Key::Comma;
  97. s_translateKey[uint8_t('>')] =
  98. s_translateKey[uint8_t('.')] = Key::Period;
  99. s_translateKey[uint8_t('?')] =
  100. s_translateKey[uint8_t('/')] = Key::Slash;
  101. s_translateKey[uint8_t('|')] =
  102. s_translateKey[uint8_t('\\')] = Key::Backslash;
  103. s_translateKey[uint8_t('0')] = Key::Key0;
  104. s_translateKey[uint8_t('1')] = Key::Key1;
  105. s_translateKey[uint8_t('2')] = Key::Key2;
  106. s_translateKey[uint8_t('3')] = Key::Key3;
  107. s_translateKey[uint8_t('4')] = Key::Key4;
  108. s_translateKey[uint8_t('5')] = Key::Key5;
  109. s_translateKey[uint8_t('6')] = Key::Key6;
  110. s_translateKey[uint8_t('7')] = Key::Key7;
  111. s_translateKey[uint8_t('8')] = Key::Key8;
  112. s_translateKey[uint8_t('9')] = Key::Key9;
  113. for (char ch = 'a'; ch <= 'z'; ++ch)
  114. {
  115. s_translateKey[uint8_t(ch)] =
  116. s_translateKey[uint8_t(ch - ' ')] = Key::KeyA + (ch - 'a');
  117. }
  118. for(int ii=0; ii<ENTRY_CONFIG_MAX_WINDOWS; ++ii)
  119. {
  120. m_window[ii] = NULL;
  121. }
  122. }
  123. NSEvent* waitEvent()
  124. {
  125. return [NSApp
  126. nextEventMatchingMask:NSEventMaskAny
  127. untilDate:[NSDate distantFuture] // wait for event
  128. inMode:NSDefaultRunLoopMode
  129. dequeue:YES
  130. ];
  131. }
  132. NSEvent* peekEvent()
  133. {
  134. return [NSApp
  135. nextEventMatchingMask:NSEventMaskAny
  136. untilDate:[NSDate distantPast] // do not wait for event
  137. inMode:NSDefaultRunLoopMode
  138. dequeue:YES
  139. ];
  140. }
  141. void getMousePos(NSWindow *window, int* outX, int* outY)
  142. {
  143. //WindowHandle handle = { 0 };
  144. //NSWindow* window = m_window[handle.idx];
  145. NSRect originalFrame = [window frame];
  146. NSPoint location = [window mouseLocationOutsideOfEventStream];
  147. NSRect adjustFrame = [window contentRectForFrameRect: originalFrame];
  148. int32_t x = location.x;
  149. int32_t y = int32_t(adjustFrame.size.height) - int32_t(location.y);
  150. // clamp within the range of the window
  151. *outX = bx::clamp(x, 0, int32_t(adjustFrame.size.width) );
  152. *outY = bx::clamp(y, 0, int32_t(adjustFrame.size.height) );
  153. }
  154. void setMousePos(NSWindow* _window, int _x, int _y)
  155. {
  156. NSRect originalFrame = [_window frame];
  157. NSRect adjustFrame = [_window contentRectForFrameRect: originalFrame];
  158. adjustFrame.origin.y = NSMaxY(NSScreen.screens[0].frame) - NSMaxY(adjustFrame);
  159. CGWarpMouseCursorPosition(CGPointMake(_x + adjustFrame.origin.x, _y + adjustFrame.origin.y));
  160. CGAssociateMouseAndMouseCursorPosition(YES);
  161. }
  162. void setMouseLock(NSWindow* _window, bool _lock)
  163. {
  164. NSWindow* newMouseLock = _lock ? _window : NULL;
  165. if ( m_mouseLock != newMouseLock )
  166. {
  167. if ( _lock )
  168. {
  169. NSRect originalFrame = [_window frame];
  170. NSRect adjustFrame = [_window contentRectForFrameRect: originalFrame];
  171. m_cmx = (int)adjustFrame.size.width / 2;
  172. m_cmy = (int)adjustFrame.size.height / 2;
  173. setMousePos(_window, m_cmx, m_cmy);
  174. [NSCursor hide];
  175. }
  176. else
  177. {
  178. [NSCursor unhide];
  179. }
  180. m_mouseLock = newMouseLock;
  181. }
  182. }
  183. uint8_t translateModifiers(int flags)
  184. {
  185. return 0
  186. | ( (0 != (flags & NX_DEVICELSHIFTKEYMASK) ) ? Modifier::LeftShift : 0)
  187. | ( (0 != (flags & NX_DEVICERSHIFTKEYMASK) ) ? Modifier::RightShift : 0)
  188. | ( (0 != (flags & NX_DEVICELALTKEYMASK) ) ? Modifier::LeftAlt : 0)
  189. | ( (0 != (flags & NX_DEVICERALTKEYMASK) ) ? Modifier::RightAlt : 0)
  190. | ( (0 != (flags & NX_DEVICELCTLKEYMASK) ) ? Modifier::LeftCtrl : 0)
  191. | ( (0 != (flags & NX_DEVICERCTLKEYMASK) ) ? Modifier::RightCtrl : 0)
  192. | ( (0 != (flags & NX_DEVICELCMDKEYMASK) ) ? Modifier::LeftMeta : 0)
  193. | ( (0 != (flags & NX_DEVICERCMDKEYMASK) ) ? Modifier::RightMeta : 0)
  194. ;
  195. }
  196. Key::Enum handleKeyEvent(NSEvent* event, uint8_t* specialKeys, uint8_t* _pressedChar)
  197. {
  198. NSString* key = [event charactersIgnoringModifiers];
  199. unichar keyChar = 0;
  200. if ([key length] == 0)
  201. {
  202. return Key::None;
  203. }
  204. keyChar = [key characterAtIndex:0];
  205. *_pressedChar = (uint8_t)keyChar;
  206. int keyCode = keyChar;
  207. *specialKeys = translateModifiers(int([event modifierFlags]));
  208. // if this is a unhandled key just return None
  209. if (keyCode < 256)
  210. {
  211. return (Key::Enum)s_translateKey[keyCode];
  212. }
  213. switch (keyCode)
  214. {
  215. case NSF1FunctionKey: return Key::F1;
  216. case NSF2FunctionKey: return Key::F2;
  217. case NSF3FunctionKey: return Key::F3;
  218. case NSF4FunctionKey: return Key::F4;
  219. case NSF5FunctionKey: return Key::F5;
  220. case NSF6FunctionKey: return Key::F6;
  221. case NSF7FunctionKey: return Key::F7;
  222. case NSF8FunctionKey: return Key::F8;
  223. case NSF9FunctionKey: return Key::F9;
  224. case NSF10FunctionKey: return Key::F10;
  225. case NSF11FunctionKey: return Key::F11;
  226. case NSF12FunctionKey: return Key::F12;
  227. case NSLeftArrowFunctionKey: return Key::Left;
  228. case NSRightArrowFunctionKey: return Key::Right;
  229. case NSUpArrowFunctionKey: return Key::Up;
  230. case NSDownArrowFunctionKey: return Key::Down;
  231. case NSPageUpFunctionKey: return Key::PageUp;
  232. case NSPageDownFunctionKey: return Key::PageDown;
  233. case NSHomeFunctionKey: return Key::Home;
  234. case NSEndFunctionKey: return Key::End;
  235. case NSPrintScreenFunctionKey: return Key::Print;
  236. }
  237. return Key::None;
  238. }
  239. bool dispatchEvent(NSEvent* event)
  240. {
  241. if (event)
  242. {
  243. NSEventType eventType = [event type];
  244. NSWindow *window = [event window];
  245. WindowHandle handle = {UINT16_MAX};
  246. if (nil != window)
  247. {
  248. handle = findHandle(window);
  249. }
  250. if (!isValid(handle))
  251. {
  252. [NSApp sendEvent:event];
  253. [NSApp updateWindows];
  254. return true;
  255. }
  256. switch (eventType)
  257. {
  258. case NSEventTypeMouseMoved:
  259. case NSEventTypeLeftMouseDragged:
  260. case NSEventTypeRightMouseDragged:
  261. case NSEventTypeOtherMouseDragged:
  262. getMousePos(window, &m_mx, &m_my);
  263. if (window == m_mouseLock)
  264. {
  265. m_mx -= m_cmx;
  266. m_my -= m_cmy;
  267. setMousePos(window, m_cmx, m_cmy);
  268. }
  269. m_eventQueue.postMouseEvent(handle, m_mx, m_my, m_scroll);
  270. break;
  271. case NSEventTypeLeftMouseDown:
  272. {
  273. // Command + Left Mouse Button acts as middle! This just a temporary solution!
  274. // This is because the average OSX user doesn't have middle mouse click.
  275. MouseButton::Enum mb = ([event modifierFlags] & NSEventModifierFlagCommand)
  276. ? MouseButton::Middle
  277. : MouseButton::Left
  278. ;
  279. m_eventQueue.postMouseEvent(handle, m_mx, m_my, m_scroll, mb, true);
  280. }
  281. break;
  282. case NSEventTypeLeftMouseUp:
  283. m_eventQueue.postMouseEvent(handle, m_mx, m_my, m_scroll, MouseButton::Left, false);
  284. m_eventQueue.postMouseEvent(handle, m_mx, m_my, m_scroll, MouseButton::Middle, false);
  285. break;
  286. case NSEventTypeRightMouseDown:
  287. m_eventQueue.postMouseEvent(handle, m_mx, m_my, m_scroll, MouseButton::Right, true);
  288. break;
  289. case NSEventTypeRightMouseUp:
  290. m_eventQueue.postMouseEvent(handle, m_mx, m_my, m_scroll, MouseButton::Right, false);
  291. break;
  292. case NSEventTypeOtherMouseDown:
  293. m_eventQueue.postMouseEvent(handle, m_mx, m_my, m_scroll, MouseButton::Middle, true);
  294. break;
  295. case NSEventTypeOtherMouseUp:
  296. m_eventQueue.postMouseEvent(handle, m_mx, m_my, m_scroll, MouseButton::Middle, false);
  297. break;
  298. case NSEventTypeScrollWheel:
  299. m_scrollf += [event deltaY];
  300. m_scroll = (int32_t)m_scrollf;
  301. m_eventQueue.postMouseEvent(handle, m_mx, m_my, m_scroll);
  302. break;
  303. case NSEventTypeKeyDown:
  304. {
  305. uint8_t modifiers = 0;
  306. uint8_t pressedChar[4];
  307. Key::Enum key = handleKeyEvent(event, &modifiers, &pressedChar[0]);
  308. // Returning false means that we take care of the key (instead of the default behavior)
  309. if (key != Key::None)
  310. {
  311. if (key == Key::KeyQ && (modifiers & Modifier::RightMeta) )
  312. {
  313. m_eventQueue.postExitEvent();
  314. }
  315. else
  316. {
  317. enum { ShiftMask = Modifier::LeftShift|Modifier::RightShift };
  318. m_eventQueue.postCharEvent(handle, 1, pressedChar);
  319. m_eventQueue.postKeyEvent(handle, key, modifiers, true);
  320. return false;
  321. }
  322. }
  323. }
  324. break;
  325. case NSEventTypeKeyUp:
  326. {
  327. uint8_t modifiers = 0;
  328. uint8_t pressedChar[4];
  329. Key::Enum key = handleKeyEvent(event, &modifiers, &pressedChar[0]);
  330. BX_UNUSED(pressedChar);
  331. if (key != Key::None)
  332. {
  333. m_eventQueue.postKeyEvent(handle, key, modifiers, false);
  334. return false;
  335. }
  336. }
  337. break;
  338. default:
  339. break;
  340. }
  341. [NSApp sendEvent:event];
  342. [NSApp updateWindows];
  343. return true;
  344. }
  345. return false;
  346. }
  347. void windowDidResize(NSWindow *window)
  348. {
  349. WindowHandle handle = findHandle(window);
  350. NSRect originalFrame = [window frame];
  351. NSRect rect = [window contentRectForFrameRect: originalFrame];
  352. uint32_t width = uint32_t(rect.size.width);
  353. uint32_t height = uint32_t(rect.size.height);
  354. m_eventQueue.postSizeEvent(handle, width, height);
  355. // Make sure mouse button state is 'up' after resize.
  356. m_eventQueue.postMouseEvent(handle, m_mx, m_my, m_scroll, MouseButton::Left, false);
  357. m_eventQueue.postMouseEvent(handle, m_mx, m_my, m_scroll, MouseButton::Right, false);
  358. }
  359. void windowDidBecomeKey(NSWindow *window)
  360. {
  361. WindowHandle handle = findHandle(window);
  362. m_eventQueue.postSuspendEvent(handle, Suspend::WillResume);
  363. m_eventQueue.postSuspendEvent(handle, Suspend::DidResume);
  364. }
  365. void windowDidResignKey(NSWindow *window)
  366. {
  367. WindowHandle handle = findHandle(window);
  368. m_eventQueue.postSuspendEvent(handle, Suspend::WillSuspend);
  369. m_eventQueue.postSuspendEvent(handle, Suspend::DidSuspend);
  370. }
  371. int32_t run(int _argc, const char* const* _argv)
  372. {
  373. [NSApplication sharedApplication];
  374. id dg = [AppDelegate sharedDelegate];
  375. [NSApp setDelegate:dg];
  376. [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
  377. [NSApp activateIgnoringOtherApps:YES];
  378. [NSApp finishLaunching];
  379. [[NSNotificationCenter defaultCenter]
  380. postNotificationName:NSApplicationWillFinishLaunchingNotification
  381. object:NSApp];
  382. [[NSNotificationCenter defaultCenter]
  383. postNotificationName:NSApplicationDidFinishLaunchingNotification
  384. object:NSApp];
  385. id quitMenuItem = [NSMenuItem new];
  386. [quitMenuItem
  387. initWithTitle:@"Quit"
  388. action:@selector(terminate:)
  389. keyEquivalent:@"q"];
  390. id appMenu = [NSMenu new];
  391. [appMenu addItem:quitMenuItem];
  392. id appMenuItem = [NSMenuItem new];
  393. [appMenuItem setSubmenu:appMenu];
  394. id menubar = [[NSMenu new] autorelease];
  395. [menubar addItem:appMenuItem];
  396. [NSApp setMainMenu:menubar];
  397. m_style = 0
  398. | NSWindowStyleMaskTitled
  399. | NSWindowStyleMaskResizable
  400. | NSWindowStyleMaskClosable
  401. | NSWindowStyleMaskMiniaturizable
  402. ;
  403. NSRect screenRect = [[NSScreen mainScreen] frame];
  404. const float centerX = (screenRect.size.width - (float)ENTRY_DEFAULT_WIDTH )*0.5f;
  405. const float centerY = (screenRect.size.height - (float)ENTRY_DEFAULT_HEIGHT)*0.5f;
  406. NSString* appName = [[NSProcessInfo processInfo] processName];
  407. createWindow(centerX, centerY, ENTRY_DEFAULT_WIDTH, ENTRY_DEFAULT_HEIGHT, ENTRY_WINDOW_FLAG_NONE, [appName UTF8String]);
  408. m_windowFrame = [m_window[0] frame];
  409. MainThreadEntry mte;
  410. mte.m_argc = _argc;
  411. mte.m_argv = _argv;
  412. bx::Thread thread;
  413. thread.init(mte.threadFunc, &mte);
  414. WindowHandle handle = { 0 };
  415. NSRect contentRect = [m_window[0] contentRectForFrameRect: m_windowFrame];
  416. uint32_t width = uint32_t(contentRect.size.width);
  417. uint32_t height = uint32_t(contentRect.size.height);
  418. m_eventQueue.postSizeEvent(handle, width, height);
  419. while (!(m_exit = [dg applicationHasTerminated]) )
  420. {
  421. bgfx::renderFrame();
  422. @autoreleasepool
  423. {
  424. while (dispatchEvent(peekEvent() ) )
  425. {
  426. }
  427. }
  428. }
  429. m_eventQueue.postExitEvent();
  430. while (bgfx::RenderFrame::NoContext != bgfx::renderFrame() ) {};
  431. thread.shutdown();
  432. return 0;
  433. }
  434. WindowHandle findHandle(NSWindow *_window)
  435. {
  436. bx::MutexScope scope(m_lock);
  437. for (uint16_t ii = 0, num = m_windowAlloc.getNumHandles(); ii < num; ++ii)
  438. {
  439. uint16_t idx = m_windowAlloc.getHandleAt(ii);
  440. if (_window == m_window[idx])
  441. {
  442. WindowHandle handle = { idx };
  443. return handle;
  444. }
  445. }
  446. WindowHandle invalid = { UINT16_MAX };
  447. return invalid;
  448. }
  449. EventQueue m_eventQueue;
  450. bx::Mutex m_lock;
  451. bx::HandleAllocT<ENTRY_CONFIG_MAX_WINDOWS> m_windowAlloc;
  452. NSWindow* m_window[ENTRY_CONFIG_MAX_WINDOWS];
  453. NSRect m_windowFrame;
  454. float m_scrollf;
  455. int32_t m_mx;
  456. int32_t m_my;
  457. int32_t m_scroll;
  458. int32_t m_style;
  459. bool m_exit;
  460. NSWindow* m_mouseLock;
  461. int32_t m_cmx;
  462. int32_t m_cmy;
  463. };
  464. static Context s_ctx;
  465. const Event* poll()
  466. {
  467. return s_ctx.m_eventQueue.poll();
  468. }
  469. const Event* poll(WindowHandle _handle)
  470. {
  471. return s_ctx.m_eventQueue.poll(_handle);
  472. }
  473. void release(const Event* _event)
  474. {
  475. s_ctx.m_eventQueue.release(_event);
  476. }
  477. WindowHandle createWindow(int32_t _x, int32_t _y, uint32_t _width, uint32_t _height, uint32_t _flags, const char* _title)
  478. {
  479. BX_UNUSED(_flags);
  480. bx::MutexScope scope(s_ctx.m_lock);
  481. WindowHandle handle = { s_ctx.m_windowAlloc.alloc() };
  482. if (UINT16_MAX != handle.idx)
  483. {
  484. void (^createWindowBlock)(void) = ^(void) {
  485. NSRect rect = NSMakeRect(_x, _y, _width, _height);
  486. NSWindow* window = [
  487. [NSWindow alloc]
  488. initWithContentRect:rect
  489. styleMask:s_ctx.m_style
  490. backing:NSBackingStoreBuffered defer:NO
  491. ];
  492. NSString* appName = [NSString stringWithUTF8String:_title];
  493. [window setTitle:appName];
  494. [window makeKeyAndOrderFront:window];
  495. [window setAcceptsMouseMovedEvents:YES];
  496. [window setBackgroundColor:[NSColor blackColor]];
  497. [[Window sharedDelegate] windowCreated:window];
  498. s_ctx.m_window[handle.idx] = window;
  499. s_ctx.m_eventQueue.postSizeEvent(handle, _width, _height);
  500. s_ctx.m_eventQueue.postWindowEvent(handle, window);
  501. };
  502. if ([NSThread isMainThread])
  503. {
  504. createWindowBlock();
  505. }
  506. else
  507. {
  508. dispatch_async(dispatch_get_main_queue(), createWindowBlock);
  509. }
  510. }
  511. return handle;
  512. }
  513. void destroyWindow(WindowHandle _handle, bool _closeWindow)
  514. {
  515. if (isValid(_handle))
  516. {
  517. dispatch_async(dispatch_get_main_queue()
  518. , ^(void){
  519. NSWindow *window = s_ctx.m_window[_handle.idx];
  520. if ( NULL != window)
  521. {
  522. s_ctx.m_eventQueue.postWindowEvent(_handle);
  523. s_ctx.m_window[_handle.idx] = NULL;
  524. if ( _closeWindow )
  525. {
  526. [window close];
  527. }
  528. if (0 == _handle.idx)
  529. {
  530. [NSApp terminate:nil];
  531. }
  532. }
  533. });
  534. bx::MutexScope scope(s_ctx.m_lock);
  535. s_ctx.m_windowAlloc.free(_handle.idx);
  536. }
  537. }
  538. void destroyWindow(WindowHandle _handle)
  539. {
  540. destroyWindow(_handle, true);
  541. }
  542. void setWindowPos(WindowHandle _handle, int32_t _x, int32_t _y)
  543. {
  544. dispatch_async(dispatch_get_main_queue()
  545. , ^{
  546. NSWindow* window = s_ctx.m_window[_handle.idx];
  547. NSScreen* screen = [window screen];
  548. NSRect screenRect = [screen frame];
  549. CGFloat menuBarHeight = [[[NSApplication sharedApplication] mainMenu] menuBarHeight];
  550. NSPoint position = { float(_x), screenRect.size.height - menuBarHeight - float(_y) };
  551. [window setFrameTopLeftPoint: position];
  552. });
  553. }
  554. void setWindowSize(WindowHandle _handle, uint32_t _width, uint32_t _height)
  555. {
  556. NSSize size = { float(_width), float(_height) };
  557. dispatch_async(dispatch_get_main_queue()
  558. , ^{
  559. [s_ctx.m_window[_handle.idx] setContentSize: size];
  560. });
  561. }
  562. void setWindowTitle(WindowHandle _handle, const char* _title)
  563. {
  564. NSString* title = [[NSString alloc] initWithCString:_title encoding:1];
  565. dispatch_async(dispatch_get_main_queue()
  566. , ^{
  567. [s_ctx.m_window[_handle.idx] setTitle: title];
  568. [title release];
  569. });
  570. }
  571. void setWindowFlags(WindowHandle _handle, uint32_t _flags, bool _enabled)
  572. {
  573. BX_UNUSED(_handle, _flags, _enabled);
  574. }
  575. void toggleFullscreen(WindowHandle _handle)
  576. {
  577. dispatch_async(dispatch_get_main_queue()
  578. , ^{
  579. NSWindow* window = s_ctx.m_window[_handle.idx];
  580. [window toggleFullScreen:nil];
  581. });
  582. }
  583. void setMouseLock(WindowHandle _handle, bool _lock)
  584. {
  585. dispatch_async(dispatch_get_main_queue()
  586. , ^{
  587. NSWindow* window = s_ctx.m_window[_handle.idx];
  588. s_ctx.setMouseLock(window, _lock);
  589. });
  590. }
  591. void* getNativeWindowHandle(WindowHandle _handle)
  592. {
  593. return s_ctx.m_window[_handle.idx];
  594. }
  595. void* getNativeDisplayHandle()
  596. {
  597. return NULL;
  598. }
  599. bgfx::NativeWindowHandleType::Enum getNativeWindowHandleType()
  600. {
  601. return bgfx::NativeWindowHandleType::Default;
  602. }
  603. } // namespace entry
  604. @implementation AppDelegate
  605. + (AppDelegate *)sharedDelegate
  606. {
  607. static id delegate = [AppDelegate new];
  608. return delegate;
  609. }
  610. - (id)init
  611. {
  612. self = [super init];
  613. if (nil == self)
  614. {
  615. return nil;
  616. }
  617. self->terminated = false;
  618. return self;
  619. }
  620. - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
  621. {
  622. BX_UNUSED(sender);
  623. self->terminated = true;
  624. return NSTerminateCancel;
  625. }
  626. - (bool)applicationHasTerminated
  627. {
  628. return self->terminated;
  629. }
  630. @end
  631. @implementation Window
  632. + (Window*)sharedDelegate
  633. {
  634. static id windowDelegate = [Window new];
  635. return windowDelegate;
  636. }
  637. - (id)init
  638. {
  639. self = [super init];
  640. if (nil == self)
  641. {
  642. return nil;
  643. }
  644. return self;
  645. }
  646. - (void)windowCreated:(NSWindow*)window
  647. {
  648. assert(window);
  649. [window setDelegate:self];
  650. }
  651. - (void)windowWillClose:(NSNotification*)notification
  652. {
  653. BX_UNUSED(notification);
  654. NSWindow *window = [notification object];
  655. [window setDelegate:nil];
  656. destroyWindow(entry::s_ctx.findHandle(window), false);
  657. }
  658. - (BOOL)windowShouldClose:(NSWindow*)window
  659. {
  660. assert(window);
  661. BX_UNUSED(window);
  662. return true;
  663. }
  664. - (void)windowDidResize:(NSNotification*)notification
  665. {
  666. NSWindow *window = [notification object];
  667. using namespace entry;
  668. s_ctx.windowDidResize(window);
  669. }
  670. - (void)windowDidBecomeKey:(NSNotification*)notification
  671. {
  672. NSWindow *window = [notification object];
  673. using namespace entry;
  674. s_ctx.windowDidBecomeKey(window);
  675. }
  676. - (void)windowDidResignKey:(NSNotification*)notification
  677. {
  678. NSWindow *window = [notification object];
  679. using namespace entry;
  680. s_ctx.windowDidResignKey(window);
  681. }
  682. @end
  683. int main(int _argc, const char* const* _argv)
  684. {
  685. using namespace entry;
  686. return s_ctx.run(_argc, _argv);
  687. }
  688. #endif // BX_PLATFORM_OSX