[Mac] More defensive programming

CocoaNativeWindow now checks for and reports failures to allocate
resources in its constructor. Additionally, it no longer calls UI
methods in the finalizer thread, as those methods will crash on Cocoa.
This commit is contained in:
thefiddler 2014-07-23 09:05:01 +02:00
parent 7cc3e922ee
commit 0a0a5d0af7

View file

@ -147,8 +147,8 @@ namespace OpenTK.Platform.MacOS
public CocoaNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device)
{
// Create the window class
Interlocked.Increment(ref UniqueId);
windowClass = Class.AllocateClass("OpenTK_GameWindow" + UniqueId, "NSWindow");
int unique_id = Interlocked.Increment(ref UniqueId);
windowClass = Class.AllocateClass("OpenTK_GameWindow" + unique_id, "NSWindow");
Class.RegisterMethod(windowClass, new WindowKeyDownDelegate(WindowKeyDown), "keyDown:", "v@:@");
Class.RegisterMethod(windowClass, new WindowDidResizeDelegate(WindowDidResize), "windowDidResize:", "v@:@");
Class.RegisterMethod(windowClass, new WindowDidMoveDelegate(WindowDidMove), "windowDidMove:", "v@:@");
@ -164,7 +164,7 @@ namespace OpenTK.Platform.MacOS
Class.RegisterMethod(windowClass, new CanBecomeMainWindowDelegate(CanBecomeMainWindow), "canBecomeMainWindow", "b@:");
Class.RegisterClass(windowClass);
IntPtr viewClass = Class.AllocateClass("OpenTK_NSView" + UniqueId, "NSView");
IntPtr viewClass = Class.AllocateClass("OpenTK_NSView" + unique_id, "NSView");
Class.RegisterMethod(viewClass, new ResetCursorRectsDelegate(ResetCursorRects), "resetCursorRects", "v@:");
Class.RegisterClass(viewClass);
@ -183,15 +183,34 @@ namespace OpenTK.Platform.MacOS
var style = GetStyleMask(windowBorder);
var bufferingType = NSBackingStore.Buffered;
IntPtr windowPtr;
windowPtr = Cocoa.SendIntPtr(windowClass, Selector.Alloc);
windowPtr = Cocoa.SendIntPtr(windowPtr, Selector.Get("initWithContentRect:styleMask:backing:defer:"), contentRect, (int)style, (int)bufferingType, false);
IntPtr classPtr;
classPtr = Cocoa.SendIntPtr(windowClass, Selector.Alloc);
if (classPtr == IntPtr.Zero)
{
Debug.Print("[Error] Failed to allocate window class.");
throw new PlatformException();
}
bool defer = false;
IntPtr windowPtr = Cocoa.SendIntPtr(classPtr, Selector.Get("initWithContentRect:styleMask:backing:defer:"), contentRect, (int)style, (int)bufferingType, defer);
if (windowPtr == IntPtr.Zero)
{
Debug.Print("[Error] Failed to initialize window with ({0}, {1}, {2}, {3}).",
contentRect, style, bufferingType, defer);
throw new PlatformException();
}
// Replace view with our custom implementation
// that overrides resetCursorRects (maybe there is
// a better way to implement this override?)
// Existing view:
IntPtr viewPtr = Cocoa.SendIntPtr(windowPtr, Selector.Get("contentView"));
if (viewPtr == IntPtr.Zero)
{
Debug.Print("[Error] Failed to retrieve content view for window {0}.", windowPtr);
throw new PlatformException();
}
// Our custom view with the same bounds:
viewPtr = Cocoa.SendIntPtr(
Cocoa.SendIntPtr(viewClass, Selector.Alloc),
@ -201,6 +220,11 @@ namespace OpenTK.Platform.MacOS
{
Cocoa.SendVoid(windowPtr, Selector.Get("setContentView:"), viewPtr);
}
else
{
Debug.Print("[Error] Failed to initialize content view with frame {0}.", selBounds);
throw new PlatformException();
}
windowInfo = new CocoaWindowInfo(windowPtr);
@ -574,7 +598,7 @@ namespace OpenTK.Platform.MacOS
if (shouldClose)
{
shouldClose = false;
CloseWindow();
CloseWindow(false);
}
}
@ -1020,14 +1044,14 @@ namespace OpenTK.Platform.MacOS
Debug.Print("Disposing of CocoaNativeWindow.");
NSApplication.Quit -= ApplicationQuit;
CursorVisible = true;
if (exists)
{
CloseWindow();
}
if (disposing)
{
CursorVisible = true;
if (exists)
{
CloseWindow(true);
}
if (trackingArea != IntPtr.Zero)
{
Cocoa.SendVoid(windowInfo.ViewHandle, selRemoveTrackingArea, trackingArea);
@ -1038,6 +1062,10 @@ namespace OpenTK.Platform.MacOS
Debug.Print("[Mac] Disposing {0}", windowInfo);
windowInfo.Dispose();
}
else
{
Debug.Print("{0} leaked, did you forget to call Dispose()?", GetType().FullName);
}
disposed = true;
OnDisposed(EventArgs.Empty);
@ -1130,13 +1158,13 @@ namespace OpenTK.Platform.MacOS
return (NSWindowStyle)Cocoa.SendUint(windowInfo.Handle, selStyleMask);
}
void CloseWindow()
void CloseWindow(bool shutdown)
{
exists = false;
// PerformClose is equivalent to pressing the close-button, which
// does not work in a borderless window. Handle this special case.
if (GetStyleMask() == NSWindowStyle.Borderless)
if (GetStyleMask() == NSWindowStyle.Borderless || shutdown)
{
if (WindowShouldClose(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero))
{