entry_osx.mm 21 KB

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