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