/* * Copyright 2011-2019 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause */ #include "entry_p.h" #if ENTRY_CONFIG_USE_NATIVE && BX_PLATFORM_OSX #import #include #include #include #include #include @interface AppDelegate : NSObject { bool terminated; } + (AppDelegate *)sharedDelegate; - (id)init; - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender; - (bool)applicationHasTerminated; @end @interface Window : NSObject { } + (Window*)sharedDelegate; - (id)init; - (void)windowCreated:(NSWindow*)window; - (void)windowWillClose:(NSNotification*)notification; - (BOOL)windowShouldClose:(NSWindow*)window; - (void)windowDidResize:(NSNotification*)notification; - (void)windowDidBecomeKey:(NSNotification *)notification; - (void)windowDidResignKey:(NSNotification *)notification; @end namespace entry { /// inline void osxSetNSWindow(void* _window, void* _nsgl = NULL) { bgfx::PlatformData pd; pd.ndt = NULL; pd.nwh = _window; pd.context = _nsgl; pd.backBuffer = NULL; pd.backBufferDS = NULL; bgfx::setPlatformData(pd); } static uint8_t s_translateKey[256]; struct MainThreadEntry { int m_argc; const char* const* m_argv; static int32_t threadFunc(bx::Thread* _thread, void* _userData) { BX_UNUSED(_thread); CFBundleRef mainBundle = CFBundleGetMainBundle(); if (mainBundle != nil) { CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle); if (resourcesURL != nil) { char path[PATH_MAX]; if (CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8*)path, PATH_MAX) ) { chdir(path); } CFRelease(resourcesURL); } } MainThreadEntry* self = (MainThreadEntry*)_userData; return main(self->m_argc, self->m_argv); } }; struct Context { Context() : m_scrollf(0.0f) , m_mx(0) , m_my(0) , m_scroll(0) , m_style(0) , m_exit(false) , m_fullscreen(false) { s_translateKey[27] = Key::Esc; s_translateKey[uint8_t('\r')] = Key::Return; s_translateKey[uint8_t('\t')] = Key::Tab; s_translateKey[127] = Key::Backspace; s_translateKey[uint8_t(' ')] = Key::Space; s_translateKey[uint8_t('+')] = s_translateKey[uint8_t('=')] = Key::Plus; s_translateKey[uint8_t('_')] = s_translateKey[uint8_t('-')] = Key::Minus; s_translateKey[uint8_t('~')] = s_translateKey[uint8_t('`')] = Key::Tilde; s_translateKey[uint8_t(':')] = s_translateKey[uint8_t(';')] = Key::Semicolon; s_translateKey[uint8_t('"')] = s_translateKey[uint8_t('\'')] = Key::Quote; s_translateKey[uint8_t('{')] = s_translateKey[uint8_t('[')] = Key::LeftBracket; s_translateKey[uint8_t('}')] = s_translateKey[uint8_t(']')] = Key::RightBracket; s_translateKey[uint8_t('<')] = s_translateKey[uint8_t(',')] = Key::Comma; s_translateKey[uint8_t('>')] = s_translateKey[uint8_t('.')] = Key::Period; s_translateKey[uint8_t('?')] = s_translateKey[uint8_t('/')] = Key::Slash; s_translateKey[uint8_t('|')] = s_translateKey[uint8_t('\\')] = Key::Backslash; s_translateKey[uint8_t('0')] = Key::Key0; s_translateKey[uint8_t('1')] = Key::Key1; s_translateKey[uint8_t('2')] = Key::Key2; s_translateKey[uint8_t('3')] = Key::Key3; s_translateKey[uint8_t('4')] = Key::Key4; s_translateKey[uint8_t('5')] = Key::Key5; s_translateKey[uint8_t('6')] = Key::Key6; s_translateKey[uint8_t('7')] = Key::Key7; s_translateKey[uint8_t('8')] = Key::Key8; s_translateKey[uint8_t('9')] = Key::Key9; for (char ch = 'a'; ch <= 'z'; ++ch) { s_translateKey[uint8_t(ch)] = s_translateKey[uint8_t(ch - ' ')] = Key::KeyA + (ch - 'a'); } for(int ii=0; ii m_windowAlloc; NSWindow* m_window[ENTRY_CONFIG_MAX_WINDOWS]; NSRect m_windowFrame; float m_scrollf; int32_t m_mx; int32_t m_my; int32_t m_scroll; int32_t m_style; bool m_exit; bool m_fullscreen; }; static Context s_ctx; const Event* poll() { return s_ctx.m_eventQueue.poll(); } const Event* poll(WindowHandle _handle) { return s_ctx.m_eventQueue.poll(_handle); } void release(const Event* _event) { s_ctx.m_eventQueue.release(_event); } WindowHandle createWindow(int32_t _x, int32_t _y, uint32_t _width, uint32_t _height, uint32_t _flags, const char* _title) { BX_UNUSED(_flags); bx::MutexScope scope(s_ctx.m_lock); WindowHandle handle = { s_ctx.m_windowAlloc.alloc() }; if (UINT16_MAX != handle.idx) { void (^createWindowBlock)(void) = ^(void) { NSRect rect = NSMakeRect(_x, _y, _width, _height); NSWindow* window = [[NSWindow alloc] initWithContentRect:rect styleMask:s_ctx.m_style backing:NSBackingStoreBuffered defer:NO ]; NSString* appName = [NSString stringWithUTF8String:_title]; [window setTitle:appName]; [window makeKeyAndOrderFront:window]; [window setAcceptsMouseMovedEvents:YES]; [window setBackgroundColor:[NSColor blackColor]]; [[Window sharedDelegate] windowCreated:window]; s_ctx.m_window[handle.idx] = window; s_ctx.m_eventQueue.postSizeEvent(handle, _width, _height); s_ctx.m_eventQueue.postWindowEvent(handle, window); }; if ([NSThread isMainThread]) { createWindowBlock(); } else { dispatch_async(dispatch_get_main_queue(), createWindowBlock); } } return handle; } void destroyWindow(WindowHandle _handle, bool _closeWindow) { if (isValid(_handle)) { dispatch_async(dispatch_get_main_queue(), ^(void) { NSWindow *window = s_ctx.m_window[_handle.idx]; if ( NULL != window) { s_ctx.m_eventQueue.postWindowEvent(_handle); s_ctx.m_window[_handle.idx] = NULL; if ( _closeWindow ) { [window close]; } if (0 == _handle.idx) { [NSApp terminate:nil]; } } }); bx::MutexScope scope(s_ctx.m_lock); s_ctx.m_windowAlloc.free(_handle.idx); } } void destroyWindow(WindowHandle _handle) { destroyWindow(_handle, true); } void setWindowPos(WindowHandle _handle, int32_t _x, int32_t _y) { dispatch_async(dispatch_get_main_queue() , ^{ NSWindow* window = s_ctx.m_window[_handle.idx]; NSScreen* screen = [window screen]; NSRect screenRect = [screen frame]; CGFloat menuBarHeight = [[[NSApplication sharedApplication] mainMenu] menuBarHeight]; NSPoint position = { float(_x), screenRect.size.height - menuBarHeight - float(_y) }; [window setFrameTopLeftPoint: position]; }); } void setWindowSize(WindowHandle _handle, uint32_t _width, uint32_t _height) { NSSize size = { float(_width), float(_height) }; dispatch_async(dispatch_get_main_queue() , ^{ [s_ctx.m_window[_handle.idx] setContentSize: size]; }); } void setWindowTitle(WindowHandle _handle, const char* _title) { NSString* title = [[NSString alloc] initWithCString:_title encoding:1]; dispatch_async(dispatch_get_main_queue() , ^{ [s_ctx.m_window[_handle.idx] setTitle: title]; [title release]; }); } void setWindowFlags(WindowHandle _handle, uint32_t _flags, bool _enabled) { BX_UNUSED(_handle, _flags, _enabled); } void toggleFullscreen(WindowHandle _handle) { dispatch_async(dispatch_get_main_queue() , ^{ NSWindow* window = s_ctx.m_window[_handle.idx]; NSScreen* screen = [window screen]; NSRect screenRect = [screen frame]; if (!s_ctx.m_fullscreen) { s_ctx.m_style &= ~NSWindowStyleMaskTitled; s_ctx.m_fullscreen = true; [NSMenu setMenuBarVisible: false]; [window setStyleMask: s_ctx.m_style]; [window setFrame:screenRect display:YES]; } else { s_ctx.m_style |= NSWindowStyleMaskTitled; s_ctx.m_fullscreen = false; [NSMenu setMenuBarVisible: true]; [window setStyleMask: s_ctx.m_style]; [window setFrame:s_ctx.m_windowFrame display:YES]; } }); } void setMouseLock(WindowHandle _handle, bool _lock) { BX_UNUSED(_handle, _lock); } } // namespace entry @implementation AppDelegate + (AppDelegate *)sharedDelegate { static id delegate = [AppDelegate new]; return delegate; } - (id)init { self = [super init]; if (nil == self) { return nil; } self->terminated = false; return self; } - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { BX_UNUSED(sender); self->terminated = true; return NSTerminateCancel; } - (bool)applicationHasTerminated { return self->terminated; } @end @implementation Window + (Window*)sharedDelegate { static id windowDelegate = [Window new]; return windowDelegate; } - (id)init { self = [super init]; if (nil == self) { return nil; } return self; } - (void)windowCreated:(NSWindow*)window { assert(window); [window setDelegate:self]; } - (void)windowWillClose:(NSNotification*)notification { BX_UNUSED(notification); NSWindow *window = [notification object]; [window setDelegate:nil]; destroyWindow(entry::s_ctx.findHandle(window), false); } - (BOOL)windowShouldClose:(NSWindow*)window { assert(window); return true; } - (void)windowDidResize:(NSNotification*)notification { NSWindow *window = [notification object]; using namespace entry; s_ctx.windowDidResize(window); } - (void)windowDidBecomeKey:(NSNotification*)notification { NSWindow *window = [notification object]; using namespace entry; s_ctx.windowDidBecomeKey(window); } - (void)windowDidResignKey:(NSNotification*)notification { NSWindow *window = [notification object]; using namespace entry; s_ctx.windowDidResignKey(window); } @end int main(int _argc, const char* const* _argv) { using namespace entry; return s_ctx.run(_argc, _argv); } #endif // BX_PLATFORM_OSX