602 lines
14 KiB
Plaintext
602 lines
14 KiB
Plaintext
/*
|
|
* Copyright 2011-2014 Branimir Karadzic. All rights reserved.
|
|
* License: http://www.opensource.org/licenses/BSD-2-Clause
|
|
*/
|
|
|
|
#include "entry_p.h"
|
|
|
|
#if ENTRY_CONFIG_USE_NATIVE && BX_PLATFORM_OSX
|
|
|
|
#import <Cocoa/Cocoa.h>
|
|
|
|
#include <bgfxplatform.h>
|
|
|
|
#include <bx/uint32_t.h>
|
|
#include <bx/thread.h>
|
|
#include <bx/os.h>
|
|
|
|
#define DEFAULT_WIDTH 1280
|
|
#define DEFAULT_HEIGHT 720
|
|
|
|
@interface AppDelegate : NSObject<NSApplicationDelegate>
|
|
{
|
|
bool terminated;
|
|
}
|
|
|
|
+ (AppDelegate *)sharedDelegate;
|
|
- (id)init;
|
|
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
|
|
- (bool)applicationHasTerminated;
|
|
|
|
@end
|
|
|
|
@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
|
|
|
|
@interface Window : NSObject<NSWindowDelegate>
|
|
{
|
|
unsigned int windowCount;
|
|
}
|
|
|
|
+ (Window *)sharedDelegate;
|
|
- (id)init;
|
|
- (void)windowCreated:(NSWindow *)window;
|
|
- (BOOL)windowShouldClose:(NSWindow *)window;
|
|
|
|
@end
|
|
|
|
@implementation Window
|
|
|
|
+ (Window *)sharedDelegate
|
|
{
|
|
static id windowDelegate = [Window new];
|
|
return windowDelegate;
|
|
}
|
|
|
|
- (id)init
|
|
{
|
|
self = [super init];
|
|
if (nil == self)
|
|
{
|
|
return nil;
|
|
}
|
|
|
|
self->windowCount = 0;
|
|
return self;
|
|
}
|
|
|
|
- (void)windowCreated:(NSWindow *)window
|
|
{
|
|
assert(window);
|
|
|
|
[window setDelegate:self];
|
|
|
|
assert(self->windowCount < ~0u);
|
|
self->windowCount += 1;
|
|
}
|
|
|
|
- (BOOL)windowShouldClose:(NSWindow *)window
|
|
{
|
|
assert(window);
|
|
|
|
[window setDelegate:nil];
|
|
|
|
assert(self->windowCount);
|
|
self->windowCount -= 1;
|
|
|
|
if (self->windowCount == 0)
|
|
{
|
|
[NSApp terminate:self];
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
@end
|
|
|
|
// Scan codes on Mac taken from http://boredzo.org/blog/archives/2007-05-22/virtual-key-codes
|
|
|
|
#define KEY_RETURN 36
|
|
#define KEY_TAB 48
|
|
#define KEY_DELETE 51
|
|
#define KEY_ESCAPE 53
|
|
|
|
namespace entry
|
|
{
|
|
static WindowHandle s_defaultWindow = { 0 }; // TODO: Add support for more windows
|
|
static uint8_t s_translateKey[256];
|
|
|
|
struct MainThreadEntry
|
|
{
|
|
int m_argc;
|
|
char** m_argv;
|
|
|
|
static int32_t threadFunc(void* _userData)
|
|
{
|
|
MainThreadEntry* self = (MainThreadEntry*)_userData;
|
|
return main(self->m_argc, self->m_argv);
|
|
}
|
|
};
|
|
|
|
struct Context
|
|
{
|
|
Context()
|
|
: m_exit(false)
|
|
{
|
|
s_translateKey[KEY_ESCAPE] = Key::Esc;
|
|
s_translateKey[KEY_RETURN] = Key::Return;
|
|
s_translateKey[KEY_TAB] = Key::Tab;
|
|
s_translateKey[KEY_DELETE] = Key::Backspace;
|
|
//s_translateKey[VK_SPACE] = Key::Space;
|
|
//s_translateKey[VK_UP] = Key::Up;
|
|
//s_translateKey[VK_DOWN] = Key::Down;
|
|
//s_translateKey[VK_LEFT] = Key::Left;
|
|
//s_translateKey[VK_RIGHT] = Key::Right;
|
|
//s_translateKey[VK_PRIOR] = Key::PageUp;
|
|
//s_translateKey[VK_NEXT] = Key::PageUp;
|
|
//s_translateKey[VK_HOME] = Key::Home;
|
|
//s_translateKey[VK_END] = Key::End;
|
|
//s_translateKey[VK_SNAPSHOT] = Key::Print;
|
|
//s_translateKey[VK_OEM_PLUS] = Key::Plus;
|
|
//s_translateKey[VK_OEM_MINUS] = Key::Minus;
|
|
//s_translateKey[VK_F1] = Key::F1;
|
|
//s_translateKey[VK_F2] = Key::F2;
|
|
//s_translateKey[VK_F3] = Key::F3;
|
|
//s_translateKey[VK_F4] = Key::F4;
|
|
//s_translateKey[VK_F5] = Key::F5;
|
|
//s_translateKey[VK_F6] = Key::F6;
|
|
//s_translateKey[VK_F7] = Key::F7;
|
|
//s_translateKey[VK_F8] = Key::F8;
|
|
//s_translateKey[VK_F9] = Key::F9;
|
|
//s_translateKey[VK_F10] = Key::F10;
|
|
//s_translateKey[VK_F11] = Key::F11;
|
|
//s_translateKey[VK_F12] = Key::F12;
|
|
//s_translateKey[VK_NUMPAD0] = Key::NumPad0;
|
|
//s_translateKey[VK_NUMPAD1] = Key::NumPad1;
|
|
//s_translateKey[VK_NUMPAD2] = Key::NumPad2;
|
|
//s_translateKey[VK_NUMPAD3] = Key::NumPad3;
|
|
//s_translateKey[VK_NUMPAD4] = Key::NumPad4;
|
|
//s_translateKey[VK_NUMPAD5] = Key::NumPad5;
|
|
//s_translateKey[VK_NUMPAD6] = Key::NumPad6;
|
|
//s_translateKey[VK_NUMPAD7] = Key::NumPad7;
|
|
//s_translateKey[VK_NUMPAD8] = Key::NumPad8;
|
|
//s_translateKey[VK_NUMPAD9] = Key::NumPad9;
|
|
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;
|
|
s_translateKey[uint8_t('A')] = Key::KeyA;
|
|
s_translateKey[uint8_t('B')] = Key::KeyB;
|
|
s_translateKey[uint8_t('C')] = Key::KeyC;
|
|
s_translateKey[uint8_t('D')] = Key::KeyD;
|
|
s_translateKey[uint8_t('E')] = Key::KeyE;
|
|
s_translateKey[uint8_t('F')] = Key::KeyF;
|
|
s_translateKey[uint8_t('G')] = Key::KeyG;
|
|
s_translateKey[uint8_t('H')] = Key::KeyH;
|
|
s_translateKey[uint8_t('I')] = Key::KeyI;
|
|
s_translateKey[uint8_t('J')] = Key::KeyJ;
|
|
s_translateKey[uint8_t('K')] = Key::KeyK;
|
|
s_translateKey[uint8_t('L')] = Key::KeyL;
|
|
s_translateKey[uint8_t('M')] = Key::KeyM;
|
|
s_translateKey[uint8_t('N')] = Key::KeyN;
|
|
s_translateKey[uint8_t('O')] = Key::KeyO;
|
|
s_translateKey[uint8_t('P')] = Key::KeyP;
|
|
s_translateKey[uint8_t('Q')] = Key::KeyQ;
|
|
s_translateKey[uint8_t('R')] = Key::KeyR;
|
|
s_translateKey[uint8_t('S')] = Key::KeyS;
|
|
s_translateKey[uint8_t('T')] = Key::KeyT;
|
|
s_translateKey[uint8_t('U')] = Key::KeyU;
|
|
s_translateKey[uint8_t('V')] = Key::KeyV;
|
|
s_translateKey[uint8_t('W')] = Key::KeyW;
|
|
s_translateKey[uint8_t('X')] = Key::KeyX;
|
|
s_translateKey[uint8_t('Y')] = Key::KeyY;
|
|
s_translateKey[uint8_t('Z')] = Key::KeyZ;
|
|
s_translateKey[uint8_t('a')] = Key::KeyA;
|
|
s_translateKey[uint8_t('b')] = Key::KeyB;
|
|
s_translateKey[uint8_t('c')] = Key::KeyC;
|
|
s_translateKey[uint8_t('d')] = Key::KeyD;
|
|
s_translateKey[uint8_t('e')] = Key::KeyE;
|
|
s_translateKey[uint8_t('f')] = Key::KeyF;
|
|
s_translateKey[uint8_t('g')] = Key::KeyG;
|
|
s_translateKey[uint8_t('h')] = Key::KeyH;
|
|
s_translateKey[uint8_t('i')] = Key::KeyI;
|
|
s_translateKey[uint8_t('j')] = Key::KeyJ;
|
|
s_translateKey[uint8_t('k')] = Key::KeyK;
|
|
s_translateKey[uint8_t('l')] = Key::KeyL;
|
|
s_translateKey[uint8_t('m')] = Key::KeyM;
|
|
s_translateKey[uint8_t('n')] = Key::KeyN;
|
|
s_translateKey[uint8_t('o')] = Key::KeyO;
|
|
s_translateKey[uint8_t('p')] = Key::KeyP;
|
|
s_translateKey[uint8_t('q')] = Key::KeyQ;
|
|
s_translateKey[uint8_t('r')] = Key::KeyR;
|
|
s_translateKey[uint8_t('s')] = Key::KeyS;
|
|
s_translateKey[uint8_t('t')] = Key::KeyT;
|
|
s_translateKey[uint8_t('u')] = Key::KeyU;
|
|
s_translateKey[uint8_t('v')] = Key::KeyV;
|
|
s_translateKey[uint8_t('w')] = Key::KeyW;
|
|
s_translateKey[uint8_t('x')] = Key::KeyX;
|
|
s_translateKey[uint8_t('y')] = Key::KeyY;
|
|
s_translateKey[uint8_t('z')] = Key::KeyZ;
|
|
}
|
|
|
|
NSEvent* waitEvent()
|
|
{
|
|
return [NSApp
|
|
nextEventMatchingMask:NSAnyEventMask
|
|
untilDate:[NSDate distantFuture] // wait for event
|
|
inMode:NSDefaultRunLoopMode
|
|
dequeue:YES
|
|
];
|
|
}
|
|
|
|
NSEvent* peekEvent()
|
|
{
|
|
return [NSApp
|
|
nextEventMatchingMask:NSAnyEventMask
|
|
untilDate:[NSDate distantPast] // do not wait for event
|
|
inMode:NSDefaultRunLoopMode
|
|
dequeue:YES
|
|
];
|
|
}
|
|
|
|
void getMousePos(int* outX, int* outY)
|
|
{
|
|
NSRect originalFrame = [m_window frame];
|
|
NSPoint location = [m_window mouseLocationOutsideOfEventStream];
|
|
NSRect adjustFrame = [NSWindow contentRectForFrameRect: originalFrame styleMask: NSTitledWindowMask];
|
|
|
|
int x = location.x;
|
|
int y = (int)adjustFrame.size.height - (int)location.y;
|
|
|
|
// clamp within the range of the window
|
|
|
|
if (x < 0) x = 0;
|
|
if (y < 0) y = 0;
|
|
if (x > (int)adjustFrame.size.width) x = (int)adjustFrame.size.width;
|
|
if (y > (int)adjustFrame.size.height) y = (int)adjustFrame.size.height;
|
|
|
|
*outX = x;
|
|
*outY = y;
|
|
}
|
|
|
|
uint8_t translateModifiers(int flags)
|
|
{
|
|
uint8_t mask = 0;
|
|
|
|
if (flags & NSShiftKeyMask)
|
|
mask |= Modifier::LeftShift | Modifier::RightShift;
|
|
|
|
if (flags & NSAlternateKeyMask)
|
|
mask |= Modifier::LeftAlt | Modifier::RightAlt;
|
|
|
|
if (flags & NSControlKeyMask)
|
|
mask |= Modifier::LeftCtrl | Modifier::RightCtrl;
|
|
|
|
if (flags & NSCommandKeyMask)
|
|
mask |= Modifier::LeftMeta | Modifier::RightMeta;
|
|
|
|
return mask;
|
|
}
|
|
|
|
Key::Enum handleKeyEvent(NSEvent* event, uint8_t* specialKeys)
|
|
{
|
|
NSString* key = [event charactersIgnoringModifiers];
|
|
unichar keyChar = 0;
|
|
if ([key length] == 0)
|
|
return Key::None;
|
|
|
|
keyChar = [key characterAtIndex:0];
|
|
|
|
int keyCode = keyChar;
|
|
*specialKeys = translateModifiers([event modifierFlags]);
|
|
|
|
// if this is a unhandled key just return None
|
|
|
|
if (keyCode >= 256)
|
|
return Key::None;
|
|
|
|
return (Key::Enum)s_translateKey[keyCode];
|
|
}
|
|
|
|
bool dispatchEvent(NSEvent* event)
|
|
{
|
|
if (event)
|
|
{
|
|
NSEventType eventType = [event type];
|
|
|
|
switch (eventType)
|
|
{
|
|
case NSMouseMoved :
|
|
{
|
|
int x, y;
|
|
|
|
getMousePos(&x, &y);
|
|
|
|
m_eventQueue.postMouseEvent(s_defaultWindow, x, y, 0);
|
|
|
|
break;
|
|
}
|
|
|
|
case NSLeftMouseDown:
|
|
{
|
|
int x, y;
|
|
|
|
getMousePos(&x, &y);
|
|
|
|
m_eventQueue.postMouseEvent(s_defaultWindow, x, y, 0, MouseButton::Left, true);
|
|
|
|
break;
|
|
}
|
|
|
|
case NSLeftMouseUp:
|
|
{
|
|
int x, y;
|
|
|
|
getMousePos(&x, &y);
|
|
|
|
m_eventQueue.postMouseEvent(s_defaultWindow, x, y, 0, MouseButton::Left, false);
|
|
|
|
break;
|
|
}
|
|
|
|
case NSRightMouseDown:
|
|
{
|
|
int x, y;
|
|
|
|
getMousePos(&x, &y);
|
|
|
|
m_eventQueue.postMouseEvent(s_defaultWindow, x, y, 0, MouseButton::Right, true);
|
|
|
|
break;
|
|
}
|
|
|
|
case NSRightMouseUp:
|
|
{
|
|
int x, y;
|
|
|
|
getMousePos(&x, &y);
|
|
|
|
m_eventQueue.postMouseEvent(s_defaultWindow, x, y, 0, MouseButton::Right, false);
|
|
|
|
break;
|
|
}
|
|
|
|
case NSKeyDown:
|
|
{
|
|
uint8_t modifiers = 0;
|
|
Key::Enum key = handleKeyEvent(event, &modifiers);
|
|
|
|
// If KeyCode is none we don't don't handle the key and special case for cmd+q (quit)
|
|
// Note that return false here means that we take care of the key (instead of the default behavior)
|
|
|
|
if (key != Key::None)
|
|
{
|
|
if (key != Key::KeyQ && !((modifiers & Modifier::RightMeta)))
|
|
{
|
|
m_eventQueue.postKeyEvent(s_defaultWindow, key, modifiers, true);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case NSKeyUp:
|
|
{
|
|
uint8_t modifiers = 0;
|
|
Key::Enum key = handleKeyEvent(event, &modifiers);
|
|
|
|
if (key != Key::None)
|
|
{
|
|
m_eventQueue.postKeyEvent(s_defaultWindow, key, modifiers, false);
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
[NSApp sendEvent:event];
|
|
[NSApp updateWindows];
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int32_t run(int _argc, char** _argv)
|
|
{
|
|
[NSApplication sharedApplication];
|
|
|
|
id dg = [AppDelegate sharedDelegate];
|
|
[NSApp setDelegate:dg];
|
|
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
|
[NSApp activateIgnoringOtherApps:YES];
|
|
[NSApp finishLaunching];
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
postNotificationName:NSApplicationWillFinishLaunchingNotification
|
|
object:NSApp];
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
postNotificationName:NSApplicationDidFinishLaunchingNotification
|
|
object:NSApp];
|
|
|
|
id quitMenuItem = [NSMenuItem new];
|
|
[quitMenuItem
|
|
initWithTitle:@"Quit"
|
|
action:@selector(terminate:)
|
|
keyEquivalent:@"q"];
|
|
|
|
id appMenu = [NSMenu new];
|
|
[appMenu addItem:quitMenuItem];
|
|
|
|
id appMenuItem = [NSMenuItem new];
|
|
[appMenuItem setSubmenu:appMenu];
|
|
|
|
id menubar = [[NSMenu new] autorelease];
|
|
[menubar addItem:appMenuItem];
|
|
[NSApp setMainMenu:menubar];
|
|
|
|
NSRect rect = NSMakeRect(0, 0, DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
|
NSWindow* window = [NSWindow alloc];
|
|
[window
|
|
initWithContentRect:rect
|
|
styleMask:0
|
|
|NSTitledWindowMask
|
|
|NSClosableWindowMask
|
|
|NSMiniaturizableWindowMask
|
|
|NSResizableWindowMask
|
|
backing:NSBackingStoreBuffered defer:NO
|
|
];
|
|
NSString* appName = [[NSProcessInfo processInfo] processName];
|
|
[window setTitle:appName];
|
|
[window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
|
|
[window makeKeyAndOrderFront:window];
|
|
// [window setContentView:nil];
|
|
[window setAcceptsMouseMovedEvents:YES];
|
|
[window setBackgroundColor:[NSColor blackColor]];
|
|
[[Window sharedDelegate] windowCreated:window];
|
|
|
|
m_window = window;
|
|
|
|
bgfx::osxSetNSWindow(window);
|
|
|
|
MainThreadEntry mte;
|
|
mte.m_argc = _argc;
|
|
mte.m_argv = _argv;
|
|
|
|
bx::Thread thread;
|
|
thread.init(mte.threadFunc, &mte);
|
|
|
|
while (!(m_exit = [dg applicationHasTerminated]) )
|
|
{
|
|
if (bgfx::RenderFrame::Exiting == bgfx::renderFrame() )
|
|
{
|
|
break;
|
|
}
|
|
|
|
while (dispatchEvent(peekEvent() ) )
|
|
{
|
|
}
|
|
}
|
|
|
|
|
|
m_eventQueue.postExitEvent();
|
|
|
|
while (bgfx::RenderFrame::NoContext != bgfx::renderFrame() ) {};
|
|
thread.shutdown();
|
|
|
|
return 0;
|
|
}
|
|
|
|
EventQueue m_eventQueue;
|
|
|
|
bool m_exit;
|
|
NSWindow* m_window;
|
|
};
|
|
|
|
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(_x, _y, _width, _height, _flags, _title);
|
|
WindowHandle handle = { UINT16_MAX };
|
|
return handle;
|
|
}
|
|
|
|
void destroyWindow(WindowHandle _handle)
|
|
{
|
|
BX_UNUSED(_handle);
|
|
}
|
|
|
|
void setWindowPos(WindowHandle _handle, int32_t _x, int32_t _y)
|
|
{
|
|
BX_UNUSED(_handle, _x, _y);
|
|
}
|
|
|
|
void setWindowSize(WindowHandle _handle, uint32_t _width, uint32_t _height)
|
|
{
|
|
BX_UNUSED(_handle, _width, _height);
|
|
}
|
|
|
|
void setWindowTitle(WindowHandle _handle, const char* _title)
|
|
{
|
|
BX_UNUSED(_handle, _title);
|
|
}
|
|
|
|
void toggleWindowFrame(WindowHandle _handle)
|
|
{
|
|
BX_UNUSED(_handle);
|
|
}
|
|
|
|
void setMouseLock(WindowHandle _handle, bool _lock)
|
|
{
|
|
BX_UNUSED(_handle, _lock);
|
|
}
|
|
|
|
} // namespace entry
|
|
|
|
int main(int _argc, char** _argv)
|
|
{
|
|
using namespace entry;
|
|
return s_ctx.run(_argc, _argv);
|
|
}
|
|
|
|
#endif // BX_PLATFORM_OSX
|