entry_osx.mm 18 KB

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