2015-05-06 00:50:57 +00:00
|
|
|
#include <AppKit/AppKit.h>
|
2015-11-01 00:06:56 +00:00
|
|
|
#include <thread>
|
2015-05-06 00:50:57 +00:00
|
|
|
|
2015-09-02 19:09:13 +00:00
|
|
|
#include "boo/IApplication.hpp"
|
2015-11-08 00:36:38 +00:00
|
|
|
#include "boo/graphicsdev/Metal.hpp"
|
2015-11-09 02:24:45 +00:00
|
|
|
#include "CocoaCommon.hpp"
|
2015-05-06 00:50:57 +00:00
|
|
|
|
2015-11-18 23:55:25 +00:00
|
|
|
#include <LogVisor/LogVisor.hpp>
|
|
|
|
|
2015-12-27 04:20:07 +00:00
|
|
|
#if !__has_feature(objc_arc)
|
|
|
|
#error ARC Required
|
|
|
|
#endif
|
|
|
|
|
2015-11-01 00:06:56 +00:00
|
|
|
namespace boo {class ApplicationCocoa;}
|
2015-05-06 00:50:57 +00:00
|
|
|
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
|
|
|
{
|
2015-11-01 00:06:56 +00:00
|
|
|
boo::ApplicationCocoa* m_app;
|
2015-05-06 01:03:49 +00:00
|
|
|
@public
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
2015-11-01 00:06:56 +00:00
|
|
|
- (id)initWithApp:(boo::ApplicationCocoa*)app;
|
2015-05-06 00:50:57 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
namespace boo
|
|
|
|
{
|
2015-11-18 23:55:25 +00:00
|
|
|
static LogVisor::LogModule Log("boo::ApplicationCocoa");
|
2016-02-24 21:07:48 +00:00
|
|
|
|
|
|
|
IWindow* _WindowCocoaNew(const SystemString& title, NSOpenGLContext* lastGLCtx,
|
|
|
|
MetalContext* metalCtx, uint32_t sampleCount);
|
|
|
|
|
2015-09-02 19:09:13 +00:00
|
|
|
class ApplicationCocoa : public IApplication
|
2015-05-06 00:50:57 +00:00
|
|
|
{
|
2015-11-01 00:06:56 +00:00
|
|
|
public:
|
2015-05-06 00:50:57 +00:00
|
|
|
IApplicationCallback& m_callback;
|
2015-12-27 04:20:07 +00:00
|
|
|
AppDelegate* m_appDelegate;
|
2015-11-01 00:06:56 +00:00
|
|
|
private:
|
2015-09-02 19:09:13 +00:00
|
|
|
const SystemString m_uniqueName;
|
|
|
|
const SystemString m_friendlyName;
|
|
|
|
const SystemString m_pname;
|
|
|
|
const std::vector<SystemString> m_args;
|
2016-02-24 21:07:48 +00:00
|
|
|
|
2015-05-06 00:50:57 +00:00
|
|
|
NSPanel* aboutPanel;
|
2016-02-24 21:07:48 +00:00
|
|
|
|
2015-11-01 00:06:56 +00:00
|
|
|
/* All windows */
|
2015-12-27 04:20:07 +00:00
|
|
|
std::unordered_map<uintptr_t, IWindow*> m_windows;
|
2016-02-24 21:07:48 +00:00
|
|
|
|
2015-11-08 00:36:38 +00:00
|
|
|
MetalContext m_metalCtx;
|
2016-02-24 21:07:48 +00:00
|
|
|
|
2015-05-06 00:50:57 +00:00
|
|
|
void _deletedWindow(IWindow* window)
|
|
|
|
{
|
2015-12-27 04:20:07 +00:00
|
|
|
m_windows.erase(window->getPlatformHandle());
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
2015-09-02 19:09:13 +00:00
|
|
|
ApplicationCocoa(IApplicationCallback& callback,
|
|
|
|
const SystemString& uniqueName,
|
|
|
|
const SystemString& friendlyName,
|
|
|
|
const SystemString& pname,
|
|
|
|
const std::vector<SystemString>& args)
|
2015-05-06 00:50:57 +00:00
|
|
|
: m_callback(callback),
|
2015-09-02 19:09:13 +00:00
|
|
|
m_uniqueName(uniqueName),
|
2015-05-06 00:50:57 +00:00
|
|
|
m_friendlyName(friendlyName),
|
|
|
|
m_pname(pname),
|
|
|
|
m_args(args)
|
|
|
|
{
|
2015-12-27 04:20:07 +00:00
|
|
|
[[NSApplication sharedApplication] setActivationPolicy:NSApplicationActivationPolicyRegular];
|
2016-02-24 21:07:48 +00:00
|
|
|
|
2015-11-01 00:06:56 +00:00
|
|
|
/* Delegate (OS X callbacks) */
|
2015-12-27 04:20:07 +00:00
|
|
|
m_appDelegate = [[AppDelegate alloc] initWithApp:this];
|
|
|
|
[[NSApplication sharedApplication] setDelegate:m_appDelegate];
|
2016-02-24 21:07:48 +00:00
|
|
|
|
2015-11-01 00:06:56 +00:00
|
|
|
/* App menu */
|
2016-01-08 00:04:37 +00:00
|
|
|
NSMenu* rootMenu = [[NSMenu alloc] initWithTitle:@"main"];
|
|
|
|
NSMenu* appMenu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:m_friendlyName.c_str()]];
|
|
|
|
NSMenuItem* fsItem = [appMenu addItemWithTitle:@"Toggle Full Screen"
|
2015-11-01 00:06:56 +00:00
|
|
|
action:@selector(toggleFs:)
|
|
|
|
keyEquivalent:@"f"];
|
|
|
|
[fsItem setKeyEquivalentModifierMask:NSCommandKeyMask];
|
2016-01-08 00:04:37 +00:00
|
|
|
[appMenu addItem:[NSMenuItem separatorItem]];
|
2016-01-14 01:20:25 +00:00
|
|
|
NSMenuItem* quitItem = [appMenu addItemWithTitle:[NSString stringWithFormat:@"Quit %s", m_friendlyName.c_str()]
|
|
|
|
action:@selector(quitApp:)
|
|
|
|
keyEquivalent:@"q"];
|
|
|
|
[quitItem setKeyEquivalentModifierMask:NSCommandKeyMask];
|
2016-01-08 00:04:37 +00:00
|
|
|
[[rootMenu addItemWithTitle:[NSString stringWithUTF8String:m_friendlyName.c_str()]
|
|
|
|
action:nil keyEquivalent:@""] setSubmenu:appMenu];
|
|
|
|
[[NSApplication sharedApplication] setMainMenu:rootMenu];
|
2016-02-24 21:07:48 +00:00
|
|
|
|
2015-11-09 02:24:45 +00:00
|
|
|
/* Determine which graphics API to use */
|
2015-11-16 22:03:46 +00:00
|
|
|
#if BOO_HAS_METAL
|
2015-11-09 02:24:45 +00:00
|
|
|
for (const SystemString& arg : args)
|
|
|
|
if (!arg.compare("--metal"))
|
|
|
|
{
|
|
|
|
m_metalCtx.m_dev = MTLCreateSystemDefaultDevice();
|
2016-01-11 22:26:40 +00:00
|
|
|
m_metalCtx.m_q = [m_metalCtx.m_dev newCommandQueue];
|
2015-11-18 23:55:25 +00:00
|
|
|
Log.report(LogVisor::Info, "using Metal renderer");
|
2015-11-09 02:24:45 +00:00
|
|
|
break;
|
|
|
|
}
|
2015-11-18 23:55:25 +00:00
|
|
|
if (!m_metalCtx.m_dev)
|
|
|
|
Log.report(LogVisor::Info, "using OpenGL renderer");
|
|
|
|
#else
|
|
|
|
Log.report(LogVisor::Info, "using OpenGL renderer");
|
2015-11-16 22:03:46 +00:00
|
|
|
#endif
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
2016-02-24 21:07:48 +00:00
|
|
|
|
2015-09-02 19:09:13 +00:00
|
|
|
EPlatformType getPlatformType() const
|
|
|
|
{
|
2015-11-21 02:16:15 +00:00
|
|
|
return EPlatformType::Cocoa;
|
2015-09-02 19:09:13 +00:00
|
|
|
}
|
2016-02-24 21:07:48 +00:00
|
|
|
|
2015-11-01 00:06:56 +00:00
|
|
|
std::thread m_clientThread;
|
|
|
|
int m_clientReturn = 0;
|
|
|
|
int run()
|
2015-09-02 19:09:13 +00:00
|
|
|
{
|
2015-11-01 00:06:56 +00:00
|
|
|
/* Spawn client thread */
|
|
|
|
m_clientThread = std::thread([&]()
|
|
|
|
{
|
|
|
|
/* Run app */
|
|
|
|
m_clientReturn = m_callback.appMain(this);
|
2016-02-24 21:07:48 +00:00
|
|
|
|
2015-11-01 00:06:56 +00:00
|
|
|
/* Cleanup here */
|
2016-01-10 08:01:09 +00:00
|
|
|
std::vector<std::unique_ptr<IWindow>> toDelete;
|
|
|
|
toDelete.reserve(m_windows.size());
|
2015-11-01 00:06:56 +00:00
|
|
|
for (auto& window : m_windows)
|
2016-01-10 08:01:09 +00:00
|
|
|
toDelete.emplace_back(window.second);
|
2015-11-01 00:06:56 +00:00
|
|
|
});
|
2016-02-24 21:07:48 +00:00
|
|
|
|
2015-11-01 00:06:56 +00:00
|
|
|
/* Already in Cocoa's event loop; return now */
|
|
|
|
return 0;
|
2015-09-02 19:09:13 +00:00
|
|
|
}
|
2016-02-24 21:07:48 +00:00
|
|
|
|
2015-05-06 00:50:57 +00:00
|
|
|
void quit()
|
|
|
|
{
|
|
|
|
[NSApp terminate:nil];
|
|
|
|
}
|
2016-02-24 21:07:48 +00:00
|
|
|
|
2015-09-02 19:09:13 +00:00
|
|
|
const SystemString& getUniqueName() const
|
|
|
|
{
|
|
|
|
return m_uniqueName;
|
|
|
|
}
|
2016-02-24 21:07:48 +00:00
|
|
|
|
2015-09-02 19:09:13 +00:00
|
|
|
const SystemString& getFriendlyName() const
|
|
|
|
{
|
|
|
|
return m_friendlyName;
|
|
|
|
}
|
2016-02-24 21:07:48 +00:00
|
|
|
|
2015-09-02 19:09:13 +00:00
|
|
|
const SystemString& getProcessName() const
|
2015-05-06 00:50:57 +00:00
|
|
|
{
|
|
|
|
return m_pname;
|
|
|
|
}
|
2016-02-24 21:07:48 +00:00
|
|
|
|
2015-09-02 19:09:13 +00:00
|
|
|
const std::vector<SystemString>& getArgs() const
|
2015-05-06 00:50:57 +00:00
|
|
|
{
|
|
|
|
return m_args;
|
|
|
|
}
|
2016-02-24 21:07:48 +00:00
|
|
|
|
|
|
|
IWindow* newWindow(const std::string& title, uint32_t sampleCount)
|
2015-05-06 00:50:57 +00:00
|
|
|
{
|
2016-02-24 21:07:48 +00:00
|
|
|
IWindow* newWindow = _WindowCocoaNew(title, m_lastGLCtx, &m_metalCtx, sampleCount);
|
2015-12-27 04:20:07 +00:00
|
|
|
m_windows[newWindow->getPlatformHandle()] = newWindow;
|
2015-11-01 00:06:56 +00:00
|
|
|
return newWindow;
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
2016-02-24 21:07:48 +00:00
|
|
|
|
2015-11-01 00:06:56 +00:00
|
|
|
/* Last GL context */
|
|
|
|
NSOpenGLContext* m_lastGLCtx = nullptr;
|
2015-05-06 00:50:57 +00:00
|
|
|
};
|
2016-02-24 21:07:48 +00:00
|
|
|
|
2015-11-01 00:06:56 +00:00
|
|
|
void _CocoaUpdateLastGLCtx(NSOpenGLContext* lastGLCtx)
|
|
|
|
{
|
|
|
|
static_cast<ApplicationCocoa*>(APP)->m_lastGLCtx = lastGLCtx;
|
|
|
|
}
|
2015-05-06 00:50:57 +00:00
|
|
|
|
2015-11-01 00:06:56 +00:00
|
|
|
IApplication* APP = nullptr;
|
|
|
|
int ApplicationRun(IApplication::EPlatformType platform,
|
|
|
|
IApplicationCallback& cb,
|
|
|
|
const SystemString& uniqueName,
|
|
|
|
const SystemString& friendlyName,
|
|
|
|
const SystemString& pname,
|
|
|
|
const std::vector<SystemString>& args,
|
|
|
|
bool singleInstance)
|
2015-05-06 00:50:57 +00:00
|
|
|
{
|
2015-11-01 00:06:56 +00:00
|
|
|
@autoreleasepool
|
2015-05-06 00:50:57 +00:00
|
|
|
{
|
2015-11-01 00:06:56 +00:00
|
|
|
if (!APP)
|
|
|
|
{
|
2015-11-21 02:16:15 +00:00
|
|
|
if (platform != IApplication::EPlatformType::Cocoa &&
|
|
|
|
platform != IApplication::EPlatformType::Auto)
|
2015-11-01 00:06:56 +00:00
|
|
|
return 1;
|
|
|
|
APP = new ApplicationCocoa(cb, uniqueName, friendlyName, pname, args);
|
|
|
|
}
|
2015-12-27 04:20:07 +00:00
|
|
|
[[NSApplication sharedApplication] run];
|
2015-11-01 00:06:56 +00:00
|
|
|
return static_cast<ApplicationCocoa*>(APP)->m_clientReturn;
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
|
|
|
}
|
2016-02-24 21:07:48 +00:00
|
|
|
|
2015-05-06 00:50:57 +00:00
|
|
|
}
|
2015-11-01 00:06:56 +00:00
|
|
|
|
|
|
|
@implementation AppDelegate
|
|
|
|
- (id)initWithApp:(boo::ApplicationCocoa*)app
|
|
|
|
{
|
|
|
|
self = [super init];
|
|
|
|
m_app = app;
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
- (void)applicationDidFinishLaunching:(NSNotification*)notification
|
|
|
|
{
|
|
|
|
(void)notification;
|
|
|
|
m_app->run();
|
|
|
|
}
|
|
|
|
- (void)applicationWillTerminate:(NSNotification*)notification
|
|
|
|
{
|
|
|
|
(void)notification;
|
|
|
|
m_app->m_callback.appQuitting(m_app);
|
|
|
|
m_app->m_clientThread.join();
|
|
|
|
}
|
|
|
|
- (BOOL)application:(NSApplication*)sender openFile:(NSString*)filename
|
|
|
|
{
|
|
|
|
std::vector<boo::SystemString> strVec;
|
|
|
|
strVec.push_back(boo::SystemString([filename UTF8String]));
|
|
|
|
m_app->m_callback.appFilesOpen(boo::APP, strVec);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
- (void)application:(NSApplication*)sender openFiles:(NSArray*)filenames
|
|
|
|
{
|
|
|
|
std::vector<boo::SystemString> strVec;
|
|
|
|
strVec.reserve([filenames count]);
|
|
|
|
for (NSString* str in filenames)
|
|
|
|
strVec.push_back(boo::SystemString([str UTF8String]));
|
|
|
|
m_app->m_callback.appFilesOpen(boo::APP, strVec);
|
|
|
|
}
|
|
|
|
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender
|
|
|
|
{
|
|
|
|
(void)sender;
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
- (IBAction)toggleFs:(id)sender
|
|
|
|
{
|
|
|
|
(void)sender;
|
|
|
|
[[NSApp keyWindow] toggleFullScreen:nil];
|
|
|
|
}
|
|
|
|
- (IBAction)quitApp:(id)sender
|
|
|
|
{
|
|
|
|
(void)sender;
|
|
|
|
[NSApp terminate:nil];
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|