shared_ptr for IWindow; better Cocoa app lifecycle

This commit is contained in:
Jack Andersen 2017-07-16 16:45:49 -10:00
parent 49afde2fb9
commit b97c82469a
5 changed files with 88 additions and 41 deletions

View File

@ -51,7 +51,7 @@ public:
virtual const std::vector<SystemString>& getArgs() const=0; virtual const std::vector<SystemString>& getArgs() const=0;
/* Constructors/initializers for sub-objects */ /* Constructors/initializers for sub-objects */
virtual IWindow* newWindow(const SystemString& title, uint32_t drawSamples)=0; virtual std::shared_ptr<IWindow> newWindow(const SystemString& title, uint32_t drawSamples)=0;
}; };

View File

@ -254,14 +254,15 @@ enum class EClipboardType
PNGImage = 3 PNGImage = 3
}; };
class IWindow class IWindow : public std::enable_shared_from_this<IWindow>
{ {
public: public:
virtual ~IWindow() {} virtual ~IWindow() = default;
virtual void setCallback(IWindowCallback* cb)=0; virtual void setCallback(IWindowCallback* cb)=0;
virtual void closeWindow()=0;
virtual void showWindow()=0; virtual void showWindow()=0;
virtual void hideWindow()=0; virtual void hideWindow()=0;

View File

@ -11,6 +11,12 @@
#error ARC Required #error ARC Required
#endif #endif
/* If set, application will terminate once client thread reaches end;
* main() will not get back control. Otherwise, main will get back control
* but App will not terminate in the normal Cocoa manner (possibly resulting
* in CoreAnimation warnings). */
#define COCOA_TERMINATE 1
namespace boo {class ApplicationCocoa;} namespace boo {class ApplicationCocoa;}
@interface AppDelegate : NSObject <NSApplicationDelegate> @interface AppDelegate : NSObject <NSApplicationDelegate>
{ {
@ -24,8 +30,8 @@ namespace boo
{ {
static logvisor::Module Log("boo::ApplicationCocoa"); static logvisor::Module Log("boo::ApplicationCocoa");
IWindow* _WindowCocoaNew(const SystemString& title, NSOpenGLContext* lastGLCtx, std::shared_ptr<IWindow> _WindowCocoaNew(const SystemString& title, NSOpenGLContext* lastGLCtx,
MetalContext* metalCtx, uint32_t sampleCount); MetalContext* metalCtx, uint32_t sampleCount);
class ApplicationCocoa : public IApplication class ApplicationCocoa : public IApplication
{ {
@ -41,7 +47,7 @@ private:
NSPanel* aboutPanel; NSPanel* aboutPanel;
/* All windows */ /* All windows */
std::unordered_map<uintptr_t, IWindow*> m_windows; std::unordered_map<uintptr_t, std::weak_ptr<IWindow>> m_windows;
MetalContext m_metalCtx; MetalContext m_metalCtx;
@ -108,6 +114,7 @@ public:
std::thread m_clientThread; std::thread m_clientThread;
int m_clientReturn = 0; int m_clientReturn = 0;
bool m_terminateNow = false;
int run() int run()
{ {
/* Spawn client thread */ /* Spawn client thread */
@ -118,11 +125,30 @@ public:
/* Run app */ /* Run app */
m_clientReturn = m_callback.appMain(this); m_clientReturn = m_callback.appMain(this);
/* Cleanup here */ /* Cleanup */
std::vector<std::unique_ptr<IWindow>> toDelete; for (auto& w : m_windows)
toDelete.reserve(m_windows.size()); if (std::shared_ptr<IWindow> window = w.second.lock())
for (auto& window : m_windows) window->closeWindow();
toDelete.emplace_back(window.second);
#if COCOA_TERMINATE
/* Continue termination */
dispatch_sync(dispatch_get_main_queue(),
^{
/* Ends modal run loop and continues Cocoa termination */
[[NSApplication sharedApplication] replyToApplicationShouldTerminate:YES];
/* If this is reached, application didn't spawn any windows
* and must be explicitly terminated */
m_terminateNow = true;
[[NSApplication sharedApplication] terminate:nil];
});
#else
/* Return control to main() */
dispatch_sync(dispatch_get_main_queue(),
^{
[[NSApplication sharedApplication] stop:nil];
});
#endif
}); });
/* Already in Cocoa's event loop; return now */ /* Already in Cocoa's event loop; return now */
@ -154,9 +180,9 @@ public:
return m_args; return m_args;
} }
IWindow* newWindow(const std::string& title, uint32_t sampleCount) std::shared_ptr<IWindow> newWindow(const std::string& title, uint32_t sampleCount)
{ {
IWindow* newWindow = _WindowCocoaNew(title, m_lastGLCtx, &m_metalCtx, sampleCount); auto newWindow = _WindowCocoaNew(title, m_lastGLCtx, &m_metalCtx, sampleCount);
m_windows[newWindow->getPlatformHandle()] = newWindow; m_windows[newWindow->getPlatformHandle()] = newWindow;
return newWindow; return newWindow;
} }
@ -187,10 +213,14 @@ int ApplicationRun(IApplication::EPlatformType platform,
if (platform != IApplication::EPlatformType::Cocoa && if (platform != IApplication::EPlatformType::Cocoa &&
platform != IApplication::EPlatformType::Auto) platform != IApplication::EPlatformType::Auto)
return 1; return 1;
/* Never deallocated to ensure window destructors have access */
APP = new ApplicationCocoa(cb, uniqueName, friendlyName, pname, args); APP = new ApplicationCocoa(cb, uniqueName, friendlyName, pname, args);
} }
[[NSApplication sharedApplication] run]; [[NSApplication sharedApplication] run];
return static_cast<ApplicationCocoa*>(APP)->m_clientReturn; ApplicationCocoa* appCocoa = static_cast<ApplicationCocoa*>(APP);
if (appCocoa->m_clientThread.joinable())
appCocoa->m_clientThread.join();
return appCocoa->m_clientReturn;
} }
} }
@ -208,12 +238,23 @@ int ApplicationRun(IApplication::EPlatformType platform,
(void)notification; (void)notification;
m_app->run(); m_app->run();
} }
- (void)applicationWillTerminate:(NSNotification*)notification #if COCOA_TERMINATE
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)app
{ {
(void)notification; (void)app;
if (m_app->m_terminateNow)
return NSTerminateNow;
m_app->m_callback.appQuitting(m_app); m_app->m_callback.appQuitting(m_app);
m_app->m_clientThread.join(); return NSTerminateLater;
} }
#else
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)app
{
(void)app;
m_app->m_callback.appQuitting(m_app);
return NSTerminateCancel;
}
#endif
- (BOOL)application:(NSApplication*)sender openFile:(NSString*)filename - (BOOL)application:(NSApplication*)sender openFile:(NSString*)filename
{ {
std::vector<boo::SystemString> strVec; std::vector<boo::SystemString> strVec;

View File

@ -17,10 +17,10 @@
namespace boo {class WindowCocoa; class GraphicsContextCocoa;} namespace boo {class WindowCocoa; class GraphicsContextCocoa;}
@interface WindowCocoaInternal : NSWindow @interface WindowCocoaInternal : NSWindow
{ {
boo::WindowCocoa* booWindow; std::shared_ptr<boo::WindowCocoa> booWindow;
id touchBarProvider; id touchBarProvider;
} }
- (id)initWithBooWindow:(boo::WindowCocoa*)bw title:(const std::string&)title; - (id)initWithBooWindow:(std::shared_ptr<boo::WindowCocoa>&)bw title:(const std::string&)title;
- (void)setFrameDefault; - (void)setFrameDefault;
- (NSRect)genFrameDefault; - (NSRect)genFrameDefault;
- (void)setTouchBarProvider:(id)provider; - (void)setTouchBarProvider:(id)provider;
@ -218,6 +218,7 @@ public:
m_dataFactory->destroyAllData(); m_dataFactory->destroyAllData();
delete m_commandQueue; delete m_commandQueue;
delete m_dataFactory; delete m_dataFactory;
printf("CONTEXT DESTROYED\n");
} }
void _setCallback(IWindowCallback* cb) void _setCallback(IWindowCallback* cb)
@ -1266,19 +1267,19 @@ static NSString* ClipboardTypes[] =
class WindowCocoa : public IWindow class WindowCocoa : public IWindow
{ {
WindowCocoaInternal* m_nsWindow; WindowCocoaInternal* m_nsWindow;
GraphicsContextCocoa* m_gfxCtx; GraphicsContextCocoa* m_gfxCtx;
EMouseCursor m_cursor = EMouseCursor::None; EMouseCursor m_cursor = EMouseCursor::None;
bool m_closed = false;
public: public:
WindowCocoa(const std::string& title, NSOpenGLContext* lastGLCtx, MetalContext* metalCtx, uint32_t sampleCount) void setup(const std::string& title, NSOpenGLContext* lastGLCtx, MetalContext* metalCtx, uint32_t sampleCount)
{ {
dispatch_sync(dispatch_get_main_queue(), dispatch_sync(dispatch_get_main_queue(),
^{ ^{
m_nsWindow = [[WindowCocoaInternal alloc] initWithBooWindow:this title:title]; std::shared_ptr<boo::WindowCocoa> windowPtr =
std::static_pointer_cast<boo::WindowCocoa>(shared_from_this());
m_nsWindow = [[WindowCocoaInternal alloc] initWithBooWindow:windowPtr title:title];
#if BOO_HAS_METAL #if BOO_HAS_METAL
if (metalCtx->m_dev) if (metalCtx->m_dev)
m_gfxCtx = static_cast<GraphicsContextCocoa*>(_GraphicsContextCocoaMetalNew(IGraphicsContext::EGraphicsAPI::Metal, m_gfxCtx = static_cast<GraphicsContextCocoa*>(_GraphicsContextCocoaMetalNew(IGraphicsContext::EGraphicsAPI::Metal,
@ -1293,14 +1294,11 @@ public:
void _clearWindow() void _clearWindow()
{ {
m_closed = true; m_nsWindow = nullptr;
} }
~WindowCocoa() ~WindowCocoa()
{ {
if (!m_closed)
[m_nsWindow orderOut:nil];
delete m_gfxCtx;
APP->_deletedWindow(this); APP->_deletedWindow(this);
} }
@ -1309,6 +1307,14 @@ public:
m_gfxCtx->_setCallback(cb); m_gfxCtx->_setCallback(cb);
} }
void closeWindow()
{
dispatch_sync(dispatch_get_main_queue(),
^{
[m_nsWindow close];
});
}
void showWindow() void showWindow()
{ {
dispatch_sync(dispatch_get_main_queue(), dispatch_sync(dispatch_get_main_queue(),
@ -1332,13 +1338,10 @@ public:
void setTitle(const std::string& title) void setTitle(const std::string& title)
{ {
if (!m_closed) dispatch_sync(dispatch_get_main_queue(),
{ ^{
dispatch_sync(dispatch_get_main_queue(), [m_nsWindow setTitle:[NSString stringWithUTF8String:title.c_str()]];
^{ });
[m_nsWindow setTitle:[NSString stringWithUTF8String:title.c_str()]];
});
}
} }
void setCursor(EMouseCursor cursor) void setCursor(EMouseCursor cursor)
@ -1555,16 +1558,18 @@ public:
}; };
IWindow* _WindowCocoaNew(const SystemString& title, NSOpenGLContext* lastGLCtx, std::shared_ptr<IWindow> _WindowCocoaNew(const SystemString& title, NSOpenGLContext* lastGLCtx,
MetalContext* metalCtx, uint32_t sampleCount) MetalContext* metalCtx, uint32_t sampleCount)
{ {
return new WindowCocoa(title, lastGLCtx, metalCtx, sampleCount); auto ret = std::make_shared<WindowCocoa>();
ret->setup(title, lastGLCtx, metalCtx, sampleCount);
return ret;
} }
} }
@implementation WindowCocoaInternal @implementation WindowCocoaInternal
- (id)initWithBooWindow:(boo::WindowCocoa *)bw title:(const boo::SystemString&)title - (id)initWithBooWindow:(std::shared_ptr<boo::WindowCocoa>&)bw title:(const boo::SystemString&)title
{ {
self = [self initWithContentRect:[self genFrameDefault] self = [self initWithContentRect:[self genFrameDefault]
styleMask:NSWindowStyleMaskTitled| styleMask:NSWindowStyleMaskTitled|
@ -1603,8 +1608,8 @@ IWindow* _WindowCocoaNew(const SystemString& title, NSOpenGLContext* lastGLCtx,
} }
- (void)close - (void)close
{ {
booWindow->_clearWindow();
[super close]; [super close];
booWindow->_clearWindow();
} }
- (BOOL)acceptsFirstResponder - (BOOL)acceptsFirstResponder
{ {

View File

@ -202,7 +202,7 @@ struct CTestWindowCallback : IWindowCallback
struct TestApplicationCallback : IApplicationCallback struct TestApplicationCallback : IApplicationCallback
{ {
IWindow* mainWindow; std::shared_ptr<IWindow> mainWindow;
boo::TestDeviceFinder devFinder; boo::TestDeviceFinder devFinder;
CTestWindowCallback windowCallback; CTestWindowCallback windowCallback;
bool running = true; bool running = true;