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;
/* 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
};
class IWindow
class IWindow : public std::enable_shared_from_this<IWindow>
{
public:
virtual ~IWindow() {}
virtual ~IWindow() = default;
virtual void setCallback(IWindowCallback* cb)=0;
virtual void closeWindow()=0;
virtual void showWindow()=0;
virtual void hideWindow()=0;

View File

@ -11,6 +11,12 @@
#error ARC Required
#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;}
@interface AppDelegate : NSObject <NSApplicationDelegate>
{
@ -24,7 +30,7 @@ namespace boo
{
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);
class ApplicationCocoa : public IApplication
@ -41,7 +47,7 @@ private:
NSPanel* aboutPanel;
/* All windows */
std::unordered_map<uintptr_t, IWindow*> m_windows;
std::unordered_map<uintptr_t, std::weak_ptr<IWindow>> m_windows;
MetalContext m_metalCtx;
@ -108,6 +114,7 @@ public:
std::thread m_clientThread;
int m_clientReturn = 0;
bool m_terminateNow = false;
int run()
{
/* Spawn client thread */
@ -118,11 +125,30 @@ public:
/* Run app */
m_clientReturn = m_callback.appMain(this);
/* Cleanup here */
std::vector<std::unique_ptr<IWindow>> toDelete;
toDelete.reserve(m_windows.size());
for (auto& window : m_windows)
toDelete.emplace_back(window.second);
/* Cleanup */
for (auto& w : m_windows)
if (std::shared_ptr<IWindow> window = w.second.lock())
window->closeWindow();
#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 */
@ -154,9 +180,9 @@ public:
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;
return newWindow;
}
@ -187,10 +213,14 @@ int ApplicationRun(IApplication::EPlatformType platform,
if (platform != IApplication::EPlatformType::Cocoa &&
platform != IApplication::EPlatformType::Auto)
return 1;
/* Never deallocated to ensure window destructors have access */
APP = new ApplicationCocoa(cb, uniqueName, friendlyName, pname, args);
}
[[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;
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_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
{
std::vector<boo::SystemString> strVec;

View File

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

View File

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