mirror of
https://github.com/Ryujinx/Opentk.git
synced 2025-01-09 01:05:40 +00:00
Merge pull request #158 from thefiddler/fuzzfix
[Mac] Improve stability under Cocoa
This commit is contained in:
commit
ed7b83178f
|
@ -150,6 +150,7 @@ namespace OpenTK.Graphics
|
|||
|
||||
implementation = factory.CreateGLContext(mode, window, shareContext, direct_rendering, major, minor, flags);
|
||||
handle_cached = ((IGraphicsContextInternal)implementation).Context;
|
||||
factory.RegisterResource(this);
|
||||
}
|
||||
|
||||
AddContext(this);
|
||||
|
|
|
@ -1,44 +1,47 @@
|
|||
#region License
|
||||
//
|
||||
// The Open Toolkit Library License
|
||||
// GraphicsContextBase.cs
|
||||
//
|
||||
// Copyright (c) 2006 - 2009 the Open Toolkit library.
|
||||
// Author:
|
||||
// Stefanos A. <stapostol@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2006-2014 Stefanos Apostolopoulos
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do
|
||||
// so, subject to the following conditions:
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using OpenTK.Platform;
|
||||
|
||||
namespace OpenTK.Graphics
|
||||
{
|
||||
// Provides the foundation for all IGraphicsContext implementations.
|
||||
abstract class GraphicsContextBase : IGraphicsContext, IGraphicsContextInternal
|
||||
abstract class GraphicsContextBase : IGraphicsContext, IGraphicsContextInternal, IEquatable<IGraphicsContextInternal>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
bool disposed;
|
||||
|
||||
|
||||
protected ContextHandle Handle;
|
||||
protected GraphicsMode Mode;
|
||||
|
||||
|
@ -106,7 +109,53 @@ namespace OpenTK.Graphics
|
|||
|
||||
#region IDisposable Members
|
||||
|
||||
public abstract void Dispose();
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected abstract void Dispose(bool disposing);
|
||||
|
||||
#if DEBUG
|
||||
~GraphicsContextBase()
|
||||
{
|
||||
Dispose(false);
|
||||
Debug.Print("[Warning] {0}:{1} leaked. Did you forget to call Dispose()?",
|
||||
GetType().FullName, Handle);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEquatable<IGraphicsContextInternal> Members
|
||||
|
||||
public bool Equals(IGraphicsContextInternal other)
|
||||
{
|
||||
return Context.Equals(other.Context);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Members
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[{0}: IsCurrent={1}, IsDisposed={2}, VSync={3}, SwapInterval={4}, GraphicsMode={5}, ErrorChecking={6}, Implementation={7}, Context={8}]",
|
||||
GetType().Name, IsCurrent, IsDisposed, VSync, SwapInterval, GraphicsMode, ErrorChecking, Implementation, Context);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Handle.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return
|
||||
obj is IGraphicsContextInternal &&
|
||||
Equals((IGraphicsContextInternal)obj);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -102,7 +102,9 @@ namespace OpenTK
|
|||
this.options = options;
|
||||
this.device = device;
|
||||
|
||||
implementation = Factory.Default.CreateNativeWindow(x, y, width, height, title, mode, options, this.device);
|
||||
IPlatformFactory factory = Factory.Default;
|
||||
implementation = factory.CreateNativeWindow(x, y, width, height, title, mode, options, this.device);
|
||||
factory.RegisterResource(this);
|
||||
|
||||
if ((options & GameWindowFlags.Fullscreen) != 0)
|
||||
{
|
||||
|
|
|
@ -113,7 +113,7 @@ namespace OpenTK.Platform.Dummy
|
|||
|
||||
#region --- IDisposable Members ---
|
||||
|
||||
public override void Dispose() { IsDisposed = true; }
|
||||
protected override void Dispose(bool disposing) { IsDisposed = true; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -180,15 +180,9 @@ namespace OpenTK.Platform.Egl
|
|||
|
||||
#region IDisposable Members
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
// Todo: cross-reference the specs. What should happen if the context is destroyed from a different
|
||||
// thread?
|
||||
protected virtual void Dispose(bool manual)
|
||||
protected override void Dispose(bool manual)
|
||||
{
|
||||
if (!IsDisposed)
|
||||
{
|
||||
|
@ -197,19 +191,10 @@ namespace OpenTK.Platform.Egl
|
|||
Egl.MakeCurrent(WindowInfo.Display, WindowInfo.Surface, WindowInfo.Surface, IntPtr.Zero);
|
||||
Egl.DestroyContext(WindowInfo.Display, HandleAsEGLContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Print("[Warning] {0}:{1} was not disposed.", this.GetType().Name, HandleAsEGLContext);
|
||||
}
|
||||
IsDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
~EglContext()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,11 @@ namespace OpenTK.Platform
|
|||
#region Constructors
|
||||
|
||||
static Factory()
|
||||
{
|
||||
Toolkit.Init();
|
||||
}
|
||||
|
||||
public Factory()
|
||||
{
|
||||
// Ensure we are correctly initialized.
|
||||
Toolkit.Init();
|
||||
|
@ -160,6 +165,11 @@ namespace OpenTK.Platform
|
|||
#pragma warning restore 612,618
|
||||
}
|
||||
|
||||
public void RegisterResource(IDisposable resource)
|
||||
{
|
||||
default_implementation.RegisterResource(resource);
|
||||
}
|
||||
|
||||
class UnsupportedPlatform : PlatformFactoryBase
|
||||
{
|
||||
#region Fields
|
||||
|
|
|
@ -55,5 +55,7 @@ namespace OpenTK.Platform
|
|||
|
||||
[Obsolete]
|
||||
Input.IJoystickDriver CreateLegacyJoystickDriver();
|
||||
|
||||
void RegisterResource(IDisposable resource);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,9 @@ namespace OpenTK.Platform.MacOS
|
|||
[DllImport (Cocoa.LibObjC)]
|
||||
extern static void objc_registerClassPair(IntPtr classToRegister);
|
||||
|
||||
[DllImport (Cocoa.LibObjC)]
|
||||
extern static void objc_disposeClassPair(IntPtr cls);
|
||||
|
||||
public static IntPtr Get(string name)
|
||||
{
|
||||
var id = objc_getClass(name);
|
||||
|
@ -75,7 +78,10 @@ namespace OpenTK.Platform.MacOS
|
|||
objc_registerClassPair(handle);
|
||||
}
|
||||
|
||||
static List<Delegate> storedDelegates = new List<Delegate>();
|
||||
public static void DisposeClass(IntPtr handle)
|
||||
{
|
||||
objc_disposeClassPair(handle);
|
||||
}
|
||||
|
||||
public static void RegisterMethod(IntPtr handle, Delegate d, string selector, string typeString)
|
||||
{
|
||||
|
@ -89,8 +95,6 @@ namespace OpenTK.Platform.MacOS
|
|||
{
|
||||
throw new ArgumentException("Could not register method " + d + " in class + " + class_getName(handle));
|
||||
}
|
||||
|
||||
storedDelegates.Add(d); // Don't let the garbage collector eat our delegates.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -209,14 +209,13 @@ namespace OpenTK.Platform.MacOS
|
|||
|
||||
fixed (byte* pBytes = b)
|
||||
{
|
||||
IntPtr nsData = Cocoa.SendIntPtr(Cocoa.SendIntPtr(Cocoa.SendIntPtr(Class.Get("NSData"), Selector.Alloc),
|
||||
Selector.Get("initWithBytes:length:"), (IntPtr)pBytes, b.Length),
|
||||
Selector.Autorelease);
|
||||
IntPtr nsData = Cocoa.SendIntPtr(Cocoa.SendIntPtr(Class.Get("NSData"), Selector.Alloc),
|
||||
Selector.Get("initWithBytes:length:"), (IntPtr)pBytes, b.Length);
|
||||
|
||||
IntPtr nsImage = Cocoa.SendIntPtr(Cocoa.SendIntPtr(Cocoa.SendIntPtr(Class.Get("NSImage"), Selector.Alloc),
|
||||
Selector.Get("initWithData:"), nsData),
|
||||
Selector.Autorelease);
|
||||
IntPtr nsImage = Cocoa.SendIntPtr(Cocoa.SendIntPtr(Class.Get("NSImage"), Selector.Alloc),
|
||||
Selector.Get("initWithData:"), nsData);
|
||||
|
||||
Cocoa.SendVoid(nsData, Selector.Release);
|
||||
return nsImage;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,9 @@
|
|||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using OpenTK.Platform.MacOS;
|
||||
|
||||
namespace OpenTK.Platform.MacOS
|
||||
|
@ -41,14 +43,21 @@ namespace OpenTK.Platform.MacOS
|
|||
|
||||
static readonly IntPtr selQuit = Selector.Get("quit");
|
||||
|
||||
internal static void Initialize()
|
||||
static readonly int ThreadId =
|
||||
System.Threading.Thread.CurrentThread.ManagedThreadId;
|
||||
|
||||
internal static void Initialize() { }
|
||||
|
||||
static NSApplication()
|
||||
{
|
||||
Cocoa.Initialize();
|
||||
|
||||
// Create the NSAutoreleasePool
|
||||
AutoreleasePool = Cocoa.SendIntPtr(Cocoa.SendIntPtr(Class.NSAutoreleasePool, Selector.Alloc), Selector.Init);
|
||||
|
||||
// Register a Quit method to be called on cmd-q
|
||||
IntPtr nsapp = Class.Get("NSApplication");
|
||||
Class.RegisterMethod(nsapp, new OnQuitDelegate(OnQuit), "quit", "v@:");
|
||||
Class.RegisterMethod(nsapp, OnQuitHandler, "quit", "v@:");
|
||||
|
||||
// Fetch the application handle
|
||||
Handle = Cocoa.SendIntPtr(nsapp, Selector.Get("sharedApplication"));
|
||||
|
@ -58,22 +67,17 @@ namespace OpenTK.Platform.MacOS
|
|||
Cocoa.SendVoid(Handle, Selector.Get("activateIgnoringOtherApps:"), true);
|
||||
|
||||
// Create the menu bar
|
||||
var menubar = Cocoa.SendIntPtr(Cocoa.SendIntPtr(Class.Get("NSMenu"), Selector.Alloc),
|
||||
Selector.Autorelease);
|
||||
|
||||
var menuItem = Cocoa.SendIntPtr(Cocoa.SendIntPtr(Class.Get("NSMenuItem"), Selector.Alloc),
|
||||
Selector.Autorelease);
|
||||
var menubar = Cocoa.SendIntPtr(Class.Get("NSMenu"), Selector.Alloc);
|
||||
var menuItem = Cocoa.SendIntPtr(Class.Get("NSMenuItem"), Selector.Alloc);
|
||||
|
||||
// Add menu item to bar, and bar to application
|
||||
Cocoa.SendIntPtr(menubar, Selector.Get("addItem:"), menuItem);
|
||||
Cocoa.SendIntPtr(Handle, Selector.Get("setMainMenu:"), menubar);
|
||||
|
||||
// Add a "Quit" menu item and bind the button.
|
||||
var appMenu = Cocoa.SendIntPtr(Cocoa.SendIntPtr(Class.Get("NSMenu"), Selector.Alloc),
|
||||
Selector.Autorelease);
|
||||
var quitMenuItem = Cocoa.SendIntPtr(Cocoa.SendIntPtr(Cocoa.SendIntPtr(Class.Get("NSMenuItem"), Selector.Alloc),
|
||||
Selector.Get("initWithTitle:action:keyEquivalent:"), Cocoa.ToNSString("Quit"), selQuit, Cocoa.ToNSString("q")),
|
||||
Selector.Autorelease);
|
||||
var appMenu = Cocoa.SendIntPtr(Class.Get("NSMenu"), Selector.Alloc);
|
||||
var quitMenuItem = Cocoa.SendIntPtr(Cocoa.SendIntPtr(Class.Get("NSMenuItem"), Selector.Alloc),
|
||||
Selector.Get("initWithTitle:action:keyEquivalent:"), Cocoa.ToNSString("Quit"), selQuit, Cocoa.ToNSString("q"));
|
||||
|
||||
Cocoa.SendIntPtr(appMenu, Selector.Get("addItem:"), quitMenuItem);
|
||||
Cocoa.SendIntPtr(menuItem, Selector.Get("setSubmenu:"), appMenu);
|
||||
|
@ -99,9 +103,27 @@ namespace OpenTK.Platform.MacOS
|
|||
Cocoa.SendVoid(settings, Selector.Release);
|
||||
}
|
||||
|
||||
internal static bool IsUIThread
|
||||
{
|
||||
get
|
||||
{
|
||||
int thread_id = Thread.CurrentThread.ManagedThreadId;
|
||||
bool is_ui_thread = thread_id == NSApplication.ThreadId;
|
||||
if (!is_ui_thread)
|
||||
{
|
||||
Debug.Print("[Warning] UI resources must be disposed in the UI thread #{0}, not #{1}.",
|
||||
NSApplication.ThreadId, thread_id);
|
||||
}
|
||||
return is_ui_thread;
|
||||
}
|
||||
}
|
||||
|
||||
internal static event EventHandler<CancelEventArgs> Quit = delegate { };
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
delegate void OnQuitDelegate(IntPtr self, IntPtr cmd);
|
||||
|
||||
static OnQuitDelegate OnQuitHandler = OnQuit;
|
||||
static void OnQuit(IntPtr self, IntPtr cmd)
|
||||
{
|
||||
var e = new CancelEventArgs();
|
||||
|
|
|
@ -326,23 +326,16 @@ namespace OpenTK
|
|||
|
||||
#region IDisposable Members
|
||||
|
||||
~CocoaContext()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
void Dispose(bool disposing)
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (IsDisposed || Handle.Handle == IntPtr.Zero)
|
||||
return;
|
||||
|
||||
Debug.Print("Disposing of Cocoa context.");
|
||||
|
||||
if (!NSApplication.IsUIThread)
|
||||
return;
|
||||
|
||||
Cocoa.SendVoid(NSOpenGLContext, Selector.Get("clearCurrentContext"));
|
||||
Cocoa.SendVoid(Handle.Handle, Selector.Get("clearDrawable"));
|
||||
Cocoa.SendVoid(Handle.Handle, Selector.Get("release"));
|
||||
|
|
|
@ -128,6 +128,7 @@ namespace OpenTK.Platform.MacOS
|
|||
private CocoaWindowInfo windowInfo;
|
||||
private IntPtr windowClass;
|
||||
private IntPtr trackingArea;
|
||||
private IntPtr current_icon_handle;
|
||||
private bool disposed = false;
|
||||
private bool exists;
|
||||
private bool cursorVisible = true;
|
||||
|
@ -145,26 +146,44 @@ namespace OpenTK.Platform.MacOS
|
|||
|
||||
public CocoaNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device)
|
||||
{
|
||||
// Create callback methods. We need to store those,
|
||||
// otherwise the GC may collect them while they are
|
||||
// still active.
|
||||
WindowKeyDownHandler = WindowKeyDown;
|
||||
WindowDidResizeHandler = WindowDidResize;
|
||||
WindowDidMoveHandler = WindowDidMove;
|
||||
WindowDidBecomeKeyHandler = WindowDidBecomeKey;
|
||||
WindowDidResignKeyHandler = WindowDidResignKey;
|
||||
WindowWillMiniaturizeHandler = WindowWillMiniaturize;
|
||||
WindowDidMiniaturizeHandler = WindowDidMiniaturize;
|
||||
WindowDidDeminiaturizeHandler = WindowDidDeminiaturize;
|
||||
WindowShouldZoomToFrameHandler = WindowShouldZoomToFrame;
|
||||
WindowShouldCloseHandler = WindowShouldClose;
|
||||
AcceptsFirstResponderHandler = AcceptsFirstResponder;
|
||||
CanBecomeKeyWindowHandler = CanBecomeKeyWindow;
|
||||
CanBecomeMainWindowHandler = CanBecomeMainWindow;
|
||||
ResetCursorRectsHandler = ResetCursorRects;
|
||||
|
||||
// Create the window class
|
||||
Interlocked.Increment(ref UniqueId);
|
||||
windowClass = Class.AllocateClass("OpenTK_GameWindow" + UniqueId, "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@:@");
|
||||
Class.RegisterMethod(windowClass, new WindowDidBecomeKeyDelegate(WindowDidBecomeKey), "windowDidBecomeKey:", "v@:@");
|
||||
Class.RegisterMethod(windowClass, new WindowDidResignKeyDelegate(WindowDidResignKey), "windowDidResignKey:", "v@:@");
|
||||
Class.RegisterMethod(windowClass, new WindowWillMiniaturizeDelegate(WindowWillMiniaturize), "windowWillMiniaturize:", "v@:@");
|
||||
Class.RegisterMethod(windowClass, new WindowDidMiniaturizeDelegate(WindowDidMiniaturize), "windowDidMiniaturize:", "v@:@");
|
||||
Class.RegisterMethod(windowClass, new WindowDidDeminiaturizeDelegate(WindowDidDeminiaturize), "windowDidDeminiaturize:", "v@:@");
|
||||
Class.RegisterMethod(windowClass, new WindowShouldZoomToFrameDelegate(WindowShouldZoomToFrame), "windowShouldZoom:toFrame:", "b@:@{NSRect={NSPoint=ff}{NSSize=ff}}");
|
||||
Class.RegisterMethod(windowClass, new WindowShouldCloseDelegate(WindowShouldClose), "windowShouldClose:", "b@:@");
|
||||
Class.RegisterMethod(windowClass, new AcceptsFirstResponderDelegate(AcceptsFirstResponder), "acceptsFirstResponder", "b@:");
|
||||
Class.RegisterMethod(windowClass, new CanBecomeKeyWindowDelegate(CanBecomeKeyWindow), "canBecomeKeyWindow", "b@:");
|
||||
Class.RegisterMethod(windowClass, new CanBecomeMainWindowDelegate(CanBecomeMainWindow), "canBecomeMainWindow", "b@:");
|
||||
int unique_id = Interlocked.Increment(ref UniqueId);
|
||||
windowClass = Class.AllocateClass("OpenTK_GameWindow" + unique_id, "NSWindow");
|
||||
Class.RegisterMethod(windowClass, WindowKeyDownHandler, "keyDown:", "v@:@");
|
||||
Class.RegisterMethod(windowClass, WindowDidResizeHandler, "windowDidResize:", "v@:@");
|
||||
Class.RegisterMethod(windowClass, WindowDidMoveHandler, "windowDidMove:", "v@:@");
|
||||
Class.RegisterMethod(windowClass, WindowDidBecomeKeyHandler, "windowDidBecomeKey:", "v@:@");
|
||||
Class.RegisterMethod(windowClass, WindowDidResignKeyHandler, "windowDidResignKey:", "v@:@");
|
||||
Class.RegisterMethod(windowClass, WindowWillMiniaturizeHandler, "windowWillMiniaturize:", "v@:@");
|
||||
Class.RegisterMethod(windowClass, WindowDidMiniaturizeHandler, "windowDidMiniaturize:", "v@:@");
|
||||
Class.RegisterMethod(windowClass, WindowDidDeminiaturizeHandler, "windowDidDeminiaturize:", "v@:@");
|
||||
Class.RegisterMethod(windowClass, WindowShouldZoomToFrameHandler, "windowShouldZoom:toFrame:", "b@:@{NSRect={NSPoint=ff}{NSSize=ff}}");
|
||||
Class.RegisterMethod(windowClass, WindowShouldCloseHandler, "windowShouldClose:", "b@:@");
|
||||
Class.RegisterMethod(windowClass, AcceptsFirstResponderHandler, "acceptsFirstResponder", "b@:");
|
||||
Class.RegisterMethod(windowClass, CanBecomeKeyWindowHandler, "canBecomeKeyWindow", "b@:");
|
||||
Class.RegisterMethod(windowClass, CanBecomeMainWindowHandler, "canBecomeMainWindow", "b@:");
|
||||
Class.RegisterClass(windowClass);
|
||||
|
||||
IntPtr viewClass = Class.AllocateClass("OpenTK_NSView" + UniqueId, "NSView");
|
||||
Class.RegisterMethod(viewClass, new ResetCursorRectsDelegate(ResetCursorRects), "resetCursorRects", "v@:");
|
||||
IntPtr viewClass = Class.AllocateClass("OpenTK_NSView" + unique_id, "NSView");
|
||||
Class.RegisterMethod(viewClass, ResetCursorRectsHandler, "resetCursorRects", "v@:");
|
||||
Class.RegisterClass(viewClass);
|
||||
|
||||
// Create window instance
|
||||
|
@ -182,15 +201,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),
|
||||
|
@ -200,6 +238,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);
|
||||
|
||||
|
@ -214,21 +257,50 @@ namespace OpenTK.Platform.MacOS
|
|||
NSApplication.Quit += ApplicationQuit;
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
delegate void WindowKeyDownDelegate(IntPtr self, IntPtr cmd, IntPtr notification);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
delegate void WindowDidResizeDelegate(IntPtr self, IntPtr cmd, IntPtr notification);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
delegate void WindowDidMoveDelegate(IntPtr self, IntPtr cmd, IntPtr notification);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
delegate void WindowDidBecomeKeyDelegate(IntPtr self, IntPtr cmd, IntPtr notification);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
delegate void WindowDidResignKeyDelegate(IntPtr self, IntPtr cmd, IntPtr notification);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
delegate void WindowWillMiniaturizeDelegate(IntPtr self, IntPtr cmd, IntPtr notification);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
delegate void WindowDidMiniaturizeDelegate(IntPtr self, IntPtr cmd, IntPtr notification);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
delegate void WindowDidDeminiaturizeDelegate(IntPtr self, IntPtr cmd, IntPtr notification);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
delegate bool WindowShouldZoomToFrameDelegate(IntPtr self, IntPtr cmd, IntPtr nsWindow, RectangleF toFrame);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
delegate bool WindowShouldCloseDelegate(IntPtr self, IntPtr cmd, IntPtr sender);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
delegate bool AcceptsFirstResponderDelegate(IntPtr self, IntPtr cmd);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
delegate bool CanBecomeKeyWindowDelegate(IntPtr self, IntPtr cmd);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
delegate bool CanBecomeMainWindowDelegate(IntPtr self, IntPtr cmd);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
delegate void ResetCursorRectsDelegate(IntPtr self, IntPtr cmd);
|
||||
|
||||
WindowKeyDownDelegate WindowKeyDownHandler;
|
||||
WindowDidResizeDelegate WindowDidResizeHandler;
|
||||
WindowDidMoveDelegate WindowDidMoveHandler;
|
||||
WindowDidBecomeKeyDelegate WindowDidBecomeKeyHandler;
|
||||
WindowDidResignKeyDelegate WindowDidResignKeyHandler;
|
||||
WindowWillMiniaturizeDelegate WindowWillMiniaturizeHandler;
|
||||
WindowDidMiniaturizeDelegate WindowDidMiniaturizeHandler;
|
||||
WindowDidDeminiaturizeDelegate WindowDidDeminiaturizeHandler;
|
||||
WindowShouldZoomToFrameDelegate WindowShouldZoomToFrameHandler;
|
||||
WindowShouldCloseDelegate WindowShouldCloseHandler;
|
||||
AcceptsFirstResponderDelegate AcceptsFirstResponderHandler;
|
||||
CanBecomeKeyWindowDelegate CanBecomeKeyWindowHandler;
|
||||
CanBecomeMainWindowDelegate CanBecomeMainWindowHandler;
|
||||
ResetCursorRectsDelegate ResetCursorRectsHandler;
|
||||
|
||||
private void WindowKeyDown(IntPtr self, IntPtr cmd, IntPtr notification)
|
||||
{
|
||||
// Steal the event to remove the "beep" sound that is normally played for unhandled key events.
|
||||
|
@ -236,7 +308,14 @@ namespace OpenTK.Platform.MacOS
|
|||
|
||||
private void WindowDidResize(IntPtr self, IntPtr cmd, IntPtr notification)
|
||||
{
|
||||
OnResize(true);
|
||||
try
|
||||
{
|
||||
OnResize(true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Print(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void OnResize(bool resetTracking)
|
||||
|
@ -258,77 +337,141 @@ namespace OpenTK.Platform.MacOS
|
|||
|
||||
private void ApplicationQuit(object sender, CancelEventArgs e)
|
||||
{
|
||||
bool close = WindowShouldClose(windowInfo.Handle, IntPtr.Zero, IntPtr.Zero);
|
||||
e.Cancel |= !close;
|
||||
try
|
||||
{
|
||||
bool close = WindowShouldClose(windowInfo.Handle, IntPtr.Zero, IntPtr.Zero);
|
||||
e.Cancel |= !close;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Print(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void WindowDidMove(IntPtr self, IntPtr cmd, IntPtr notification)
|
||||
{
|
||||
// Problem: Called only when you stop moving for a brief moment,
|
||||
// not each frame as it is on PC.
|
||||
OnMove(EventArgs.Empty);
|
||||
try
|
||||
{
|
||||
// Problem: Called only when you stop moving for a brief moment,
|
||||
// not each frame as it is on PC.
|
||||
OnMove(EventArgs.Empty);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Print(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void WindowDidBecomeKey(IntPtr self, IntPtr cmd, IntPtr notification)
|
||||
{
|
||||
OnFocusedChanged(EventArgs.Empty);
|
||||
try
|
||||
{
|
||||
OnFocusedChanged(EventArgs.Empty);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Print(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void WindowDidResignKey(IntPtr self, IntPtr cmd, IntPtr notification)
|
||||
{
|
||||
OnFocusedChanged(EventArgs.Empty);
|
||||
try
|
||||
{
|
||||
OnFocusedChanged(EventArgs.Empty);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Print(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void WindowWillMiniaturize(IntPtr self, IntPtr cmd, IntPtr notification)
|
||||
{
|
||||
// Can get stuck in weird states if we maximize, then minimize;
|
||||
// restoring to the old state would override the normalBounds.
|
||||
// To avoid this without adding complexity, just restore state here.
|
||||
RestoreWindowState(); // Avoid getting in weird states
|
||||
try
|
||||
{
|
||||
// Can get stuck in weird states if we maximize, then minimize;
|
||||
// restoring to the old state would override the normalBounds.
|
||||
// To avoid this without adding complexity, just restore state here.
|
||||
RestoreWindowState(); // Avoid getting in weird states
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Print(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void WindowDidMiniaturize(IntPtr self, IntPtr cmd, IntPtr notification)
|
||||
{
|
||||
windowState = WindowState.Minimized;
|
||||
OnWindowStateChanged(EventArgs.Empty);
|
||||
OnResize(false); // Don't set tracking area when we minimize
|
||||
try
|
||||
{
|
||||
windowState = WindowState.Minimized;
|
||||
OnWindowStateChanged(EventArgs.Empty);
|
||||
OnResize(false); // Don't set tracking area when we minimize
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Print(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void WindowDidDeminiaturize(IntPtr self, IntPtr cmd, IntPtr notification)
|
||||
{
|
||||
windowState = WindowState.Normal;
|
||||
OnWindowStateChanged(EventArgs.Empty);
|
||||
OnResize(true);
|
||||
try
|
||||
{
|
||||
windowState = WindowState.Normal;
|
||||
OnWindowStateChanged(EventArgs.Empty);
|
||||
OnResize(true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Print(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private bool WindowShouldZoomToFrame(IntPtr self, IntPtr cmd, IntPtr nsWindow, RectangleF toFrame)
|
||||
{
|
||||
if (windowState == WindowState.Maximized)
|
||||
try
|
||||
{
|
||||
WindowState = WindowState.Normal;
|
||||
if (windowState == WindowState.Maximized)
|
||||
{
|
||||
WindowState = WindowState.Normal;
|
||||
}
|
||||
else
|
||||
{
|
||||
previousBounds = InternalBounds;
|
||||
previousWindowBorder = WindowBorder;
|
||||
|
||||
InternalBounds = toFrame;
|
||||
windowState = WindowState.Maximized;
|
||||
|
||||
OnWindowStateChanged(EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception e)
|
||||
{
|
||||
previousBounds = InternalBounds;
|
||||
previousWindowBorder = WindowBorder;
|
||||
|
||||
InternalBounds = toFrame;
|
||||
windowState = WindowState.Maximized;
|
||||
|
||||
OnWindowStateChanged(EventArgs.Empty);
|
||||
Debug.Print(e.ToString());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool WindowShouldClose(IntPtr self, IntPtr cmd, IntPtr sender)
|
||||
{
|
||||
var cancelArgs = new CancelEventArgs();
|
||||
OnClosing(cancelArgs);
|
||||
|
||||
if (!cancelArgs.Cancel)
|
||||
try
|
||||
{
|
||||
OnClosed(EventArgs.Empty);
|
||||
return true;
|
||||
var cancelArgs = new CancelEventArgs();
|
||||
OnClosing(cancelArgs);
|
||||
|
||||
if (!cancelArgs.Cancel)
|
||||
{
|
||||
OnClosed(EventArgs.Empty);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Print(e.ToString());
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -351,24 +494,31 @@ namespace OpenTK.Platform.MacOS
|
|||
|
||||
private void ResetTrackingArea()
|
||||
{
|
||||
var owner = windowInfo.ViewHandle;
|
||||
if (trackingArea != IntPtr.Zero)
|
||||
try
|
||||
{
|
||||
Cocoa.SendVoid(owner, selRemoveTrackingArea, trackingArea);
|
||||
Cocoa.SendVoid(trackingArea, Selector.Release);
|
||||
var owner = windowInfo.ViewHandle;
|
||||
if (trackingArea != IntPtr.Zero)
|
||||
{
|
||||
Cocoa.SendVoid(owner, selRemoveTrackingArea, trackingArea);
|
||||
Cocoa.SendVoid(trackingArea, Selector.Release);
|
||||
}
|
||||
|
||||
var ownerBounds = Cocoa.SendRect(owner, selBounds);
|
||||
var options = (int)(
|
||||
NSTrackingAreaOptions.MouseEnteredAndExited |
|
||||
NSTrackingAreaOptions.ActiveInKeyWindow |
|
||||
NSTrackingAreaOptions.MouseMoved |
|
||||
NSTrackingAreaOptions.CursorUpdate);
|
||||
|
||||
trackingArea = Cocoa.SendIntPtr(Cocoa.SendIntPtr(Class.Get("NSTrackingArea"), Selector.Alloc),
|
||||
selInitWithRect, ownerBounds, options, owner, IntPtr.Zero);
|
||||
|
||||
Cocoa.SendVoid(owner, selAddTrackingArea, trackingArea);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Print(e.ToString());
|
||||
}
|
||||
|
||||
var ownerBounds = Cocoa.SendRect(owner, selBounds);
|
||||
var options = (int)(
|
||||
NSTrackingAreaOptions.MouseEnteredAndExited |
|
||||
NSTrackingAreaOptions.ActiveInKeyWindow |
|
||||
NSTrackingAreaOptions.MouseMoved |
|
||||
NSTrackingAreaOptions.CursorUpdate);
|
||||
|
||||
trackingArea = Cocoa.SendIntPtr(Cocoa.SendIntPtr(Class.Get("NSTrackingArea"), Selector.Alloc),
|
||||
selInitWithRect, ownerBounds, options, owner, IntPtr.Zero);
|
||||
|
||||
Cocoa.SendVoid(owner, selAddTrackingArea, trackingArea);
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
|
@ -573,20 +723,7 @@ namespace OpenTK.Platform.MacOS
|
|||
if (shouldClose)
|
||||
{
|
||||
shouldClose = 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 (WindowShouldClose(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero))
|
||||
{
|
||||
Cocoa.SendVoid(windowInfo.Handle, selClose);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Cocoa.SendVoid(windowInfo.Handle, selPerformClose, windowInfo.Handle);
|
||||
}
|
||||
CloseWindow(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -613,13 +750,35 @@ namespace OpenTK.Platform.MacOS
|
|||
get { return icon; }
|
||||
set
|
||||
{
|
||||
icon = value;
|
||||
using (Image img = icon.ToBitmap())
|
||||
if (value != null && value != icon)
|
||||
{
|
||||
IntPtr nsimg = Cocoa.ToNSImage(img);
|
||||
Cocoa.SendVoid(NSApplication.Handle, selSetApplicationIconImage, nsimg);
|
||||
// Create and set new icon
|
||||
IntPtr nsimg = IntPtr.Zero;
|
||||
using (Image img = value.ToBitmap())
|
||||
{
|
||||
nsimg = Cocoa.ToNSImage(img);
|
||||
if (nsimg != IntPtr.Zero)
|
||||
{
|
||||
Cocoa.SendVoid(NSApplication.Handle, selSetApplicationIconImage, nsimg);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Print("[Mac] Failed to create NSImage for {0}", value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Release previous icon
|
||||
if (current_icon_handle != IntPtr.Zero)
|
||||
{
|
||||
Cocoa.SendVoid(current_icon_handle, Selector.Release);
|
||||
}
|
||||
|
||||
// Raise IconChanged event
|
||||
current_icon_handle = nsimg;
|
||||
icon = value;
|
||||
OnIconChanged(EventArgs.Empty);
|
||||
}
|
||||
OnIconChanged(EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -889,21 +1048,19 @@ namespace OpenTK.Platform.MacOS
|
|||
// effect on output quality."
|
||||
IntPtr imgdata =
|
||||
Cocoa.SendIntPtr(
|
||||
Cocoa.SendIntPtr(
|
||||
Cocoa.SendIntPtr(NSBitmapImageRep, Selector.Alloc),
|
||||
selInitWithBitmapDataPlanes,
|
||||
IntPtr.Zero,
|
||||
cursor.Width,
|
||||
cursor.Height,
|
||||
8,
|
||||
4,
|
||||
1,
|
||||
0,
|
||||
NSDeviceRGBColorSpace,
|
||||
NSBitmapFormat.AlphaFirst,
|
||||
4 * cursor.Width,
|
||||
32),
|
||||
Selector.Autorelease);
|
||||
Cocoa.SendIntPtr(NSBitmapImageRep, Selector.Alloc),
|
||||
selInitWithBitmapDataPlanes,
|
||||
IntPtr.Zero,
|
||||
cursor.Width,
|
||||
cursor.Height,
|
||||
8,
|
||||
4,
|
||||
1,
|
||||
0,
|
||||
NSDeviceRGBColorSpace,
|
||||
NSBitmapFormat.AlphaFirst,
|
||||
4 * cursor.Width,
|
||||
32);
|
||||
if (imgdata == IntPtr.Zero)
|
||||
{
|
||||
Debug.Print("Failed to create NSBitmapImageRep with size ({0},{1]})",
|
||||
|
@ -935,14 +1092,13 @@ namespace OpenTK.Platform.MacOS
|
|||
// Construct the actual NSImage
|
||||
IntPtr img =
|
||||
Cocoa.SendIntPtr(
|
||||
Cocoa.SendIntPtr(
|
||||
Cocoa.SendIntPtr(NSImage, Selector.Alloc),
|
||||
selInitWithSize,
|
||||
new SizeF(cursor.Width, cursor.Height)),
|
||||
Selector.Autorelease);
|
||||
Cocoa.SendIntPtr(NSImage, Selector.Alloc),
|
||||
selInitWithSize,
|
||||
new SizeF(cursor.Width, cursor.Height));
|
||||
if (img == IntPtr.Zero)
|
||||
{
|
||||
Debug.Print("Failed to construct NSImage from NSBitmapImageRep");
|
||||
Cocoa.SendVoid(imgdata, Selector.Release);
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
Cocoa.SendVoid(img, selAddRepresentation, imgdata);
|
||||
|
@ -950,14 +1106,13 @@ namespace OpenTK.Platform.MacOS
|
|||
// Convert the NSImage to a NSCursor
|
||||
IntPtr nscursor =
|
||||
Cocoa.SendIntPtr(
|
||||
Cocoa.SendIntPtr(
|
||||
Cocoa.SendIntPtr(NSCursor, Selector.Alloc),
|
||||
selInitWithImageHotSpot,
|
||||
img,
|
||||
new PointF(cursor.X, cursor.Y)
|
||||
),
|
||||
Selector.Autorelease);
|
||||
Cocoa.SendIntPtr(NSCursor, Selector.Alloc),
|
||||
selInitWithImageHotSpot,
|
||||
img,
|
||||
new PointF(cursor.X, cursor.Y));
|
||||
|
||||
Cocoa.SendVoid(imgdata, Selector.Release);
|
||||
Cocoa.SendVoid(img, Selector.Release);
|
||||
return nscursor;
|
||||
}
|
||||
|
||||
|
@ -994,15 +1149,15 @@ namespace OpenTK.Platform.MacOS
|
|||
get { return cursorVisible; }
|
||||
set
|
||||
{
|
||||
cursorVisible = value;
|
||||
if (value)
|
||||
if (value && !cursorVisible)
|
||||
{
|
||||
SetCursorVisible(true);
|
||||
}
|
||||
else
|
||||
else if (!value && cursorVisible)
|
||||
{
|
||||
SetCursorVisible(false);
|
||||
}
|
||||
cursorVisible = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1011,15 +1166,21 @@ namespace OpenTK.Platform.MacOS
|
|||
if (disposed)
|
||||
return;
|
||||
|
||||
Debug.Print("Disposing of CocoaNativeWindow.");
|
||||
NSApplication.Quit -= ApplicationQuit;
|
||||
Debug.Print("Disposing of CocoaNativeWindow (disposing={0}).", disposing);
|
||||
|
||||
CursorVisible = true;
|
||||
disposed = true;
|
||||
exists = false;
|
||||
if (!NSApplication.IsUIThread)
|
||||
return;
|
||||
|
||||
NSApplication.Quit -= ApplicationQuit;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
CursorVisible = true;
|
||||
if (exists)
|
||||
{
|
||||
CloseWindow(true);
|
||||
}
|
||||
|
||||
if (trackingArea != IntPtr.Zero)
|
||||
{
|
||||
Cocoa.SendVoid(windowInfo.ViewHandle, selRemoveTrackingArea, trackingArea);
|
||||
|
@ -1027,9 +1188,15 @@ namespace OpenTK.Platform.MacOS
|
|||
trackingArea = IntPtr.Zero;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1119,5 +1286,28 @@ namespace OpenTK.Platform.MacOS
|
|||
{
|
||||
return (NSWindowStyle)Cocoa.SendUint(windowInfo.Handle, selStyleMask);
|
||||
}
|
||||
|
||||
void CloseWindow(bool shutdown)
|
||||
{
|
||||
if (!Exists)
|
||||
return;
|
||||
|
||||
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 || shutdown)
|
||||
{
|
||||
if (WindowShouldClose(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero))
|
||||
{
|
||||
Cocoa.SendVoid(windowInfo.Handle, selClose);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Cocoa.SendVoid(windowInfo.Handle, selPerformClose, windowInfo.Handle);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ namespace OpenTK.Platform.MacOS
|
|||
new Dictionary<IOHIDElementRef, JoystickHat>(new IntPtrEqualityComparer());
|
||||
}
|
||||
|
||||
readonly IOHIDManagerRef hidmanager;
|
||||
IOHIDManagerRef hidmanager;
|
||||
|
||||
readonly Dictionary<IntPtr, MouseData> MouseDevices =
|
||||
new Dictionary<IntPtr, MouseData>(new IntPtrEqualityComparer());
|
||||
|
@ -94,7 +94,7 @@ namespace OpenTK.Platform.MacOS
|
|||
readonly Dictionary<int, IntPtr> JoystickIndexToDevice =
|
||||
new Dictionary<int, IntPtr>();
|
||||
|
||||
readonly CFRunLoop RunLoop = CF.CFRunLoopGetMain();
|
||||
readonly CFRunLoop RunLoop;
|
||||
readonly CFString InputLoopMode = CF.RunLoopModeDefault;
|
||||
readonly CFDictionary DeviceTypes = new CFDictionary();
|
||||
|
||||
|
@ -118,12 +118,28 @@ namespace OpenTK.Platform.MacOS
|
|||
{
|
||||
Debug.Print("Using HIDInput.");
|
||||
|
||||
RunLoop = CF.CFRunLoopGetMain();
|
||||
if (RunLoop == IntPtr.Zero)
|
||||
RunLoop = CF.CFRunLoopGetCurrent();
|
||||
if (RunLoop == IntPtr.Zero)
|
||||
{
|
||||
Debug.Print("[Error] No CFRunLoop found for {0}", GetType().FullName);
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
CF.CFRetain(RunLoop);
|
||||
|
||||
HandleDeviceAdded = DeviceAdded;
|
||||
HandleDeviceRemoved = DeviceRemoved;
|
||||
HandleDeviceValueReceived = DeviceValueReceived;
|
||||
|
||||
// For retrieving input directly from the hardware
|
||||
hidmanager = CreateHIDManager();
|
||||
if (hidmanager == IntPtr.Zero)
|
||||
{
|
||||
Debug.Print("[Mac] Failed to create IO HID manager, HIDInput driver not supported.");
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
RegisterHIDCallbacks(hidmanager);
|
||||
|
||||
// For retrieving the global cursor position
|
||||
|
@ -164,53 +180,61 @@ namespace OpenTK.Platform.MacOS
|
|||
IntPtr @event,
|
||||
IntPtr refcon)
|
||||
{
|
||||
CursorState.SetIsConnected(true);
|
||||
|
||||
switch (type)
|
||||
try
|
||||
{
|
||||
case CGEventType.MouseMoved:
|
||||
case CGEventType.LeftMouseDragged:
|
||||
case CGEventType.RightMouseDragged:
|
||||
case CGEventType.OtherMouseDragged:
|
||||
{
|
||||
Carbon.HIPoint p = CG.EventGetLocation(@event);
|
||||
CursorState.X = (int)Math.Round(p.X);
|
||||
CursorState.Y = (int)Math.Round(p.Y);
|
||||
}
|
||||
break;
|
||||
CursorState.SetIsConnected(true);
|
||||
|
||||
case CGEventType.ScrollWheel:
|
||||
{
|
||||
// Note: OpenTK follows the win32 convention, where
|
||||
// (+h, +v) = (right, up). MacOS reports (+h, +v) = (left, up)
|
||||
// so we need to flip the horizontal scroll direction.
|
||||
double h = CG.EventGetDoubleValueField(@event, CGEventField.ScrollWheelEventPointDeltaAxis2) * MacOSFactory.ScrollFactor;
|
||||
double v = CG.EventGetDoubleValueField(@event, CGEventField.ScrollWheelEventPointDeltaAxis1) * MacOSFactory.ScrollFactor;
|
||||
CursorState.SetScrollRelative((float)(-h), (float)v);
|
||||
}
|
||||
break;
|
||||
switch (type)
|
||||
{
|
||||
case CGEventType.MouseMoved:
|
||||
case CGEventType.LeftMouseDragged:
|
||||
case CGEventType.RightMouseDragged:
|
||||
case CGEventType.OtherMouseDragged:
|
||||
{
|
||||
Carbon.HIPoint p = CG.EventGetLocation(@event);
|
||||
CursorState.X = (int)Math.Round(p.X);
|
||||
CursorState.Y = (int)Math.Round(p.Y);
|
||||
}
|
||||
break;
|
||||
|
||||
case CGEventType.LeftMouseDown:
|
||||
case CGEventType.RightMouseDown:
|
||||
case CGEventType.OtherMouseDown:
|
||||
{
|
||||
int n = CG.EventGetIntegerValueField(@event, CGEventField.MouseEventButtonNumber);
|
||||
n = n == 1 ? 2 : n == 2 ? 1 : n; // flip middle and right button numbers to match OpenTK
|
||||
MouseButton b = MouseButton.Left + n;
|
||||
CursorState[b] = true;
|
||||
}
|
||||
break;
|
||||
case CGEventType.ScrollWheel:
|
||||
{
|
||||
// Note: OpenTK follows the win32 convention, where
|
||||
// (+h, +v) = (right, up). MacOS reports (+h, +v) = (left, up)
|
||||
// so we need to flip the horizontal scroll direction.
|
||||
double h = CG.EventGetDoubleValueField(@event, CGEventField.ScrollWheelEventPointDeltaAxis2) * MacOSFactory.ScrollFactor;
|
||||
double v = CG.EventGetDoubleValueField(@event, CGEventField.ScrollWheelEventPointDeltaAxis1) * MacOSFactory.ScrollFactor;
|
||||
CursorState.SetScrollRelative((float)(-h), (float)v);
|
||||
}
|
||||
break;
|
||||
|
||||
case CGEventType.LeftMouseUp:
|
||||
case CGEventType.RightMouseUp:
|
||||
case CGEventType.OtherMouseUp:
|
||||
{
|
||||
int n = CG.EventGetIntegerValueField(@event, CGEventField.MouseEventButtonNumber);
|
||||
n = n == 1 ? 2 : n == 2 ? 1 : n; // flip middle and right button numbers to match OpenTK
|
||||
MouseButton b = MouseButton.Left + n;
|
||||
CursorState[b] = false;
|
||||
}
|
||||
break;
|
||||
case CGEventType.LeftMouseDown:
|
||||
case CGEventType.RightMouseDown:
|
||||
case CGEventType.OtherMouseDown:
|
||||
{
|
||||
int n = CG.EventGetIntegerValueField(@event, CGEventField.MouseEventButtonNumber);
|
||||
n = n == 1 ? 2 : n == 2 ? 1 : n; // flip middle and right button numbers to match OpenTK
|
||||
MouseButton b = MouseButton.Left + n;
|
||||
CursorState[b] = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case CGEventType.LeftMouseUp:
|
||||
case CGEventType.RightMouseUp:
|
||||
case CGEventType.OtherMouseUp:
|
||||
{
|
||||
int n = CG.EventGetIntegerValueField(@event, CGEventField.MouseEventButtonNumber);
|
||||
n = n == 1 ? 2 : n == 2 ? 1 : n; // flip middle and right button numbers to match OpenTK
|
||||
MouseButton b = MouseButton.Left + n;
|
||||
CursorState[b] = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Do not let any exceptions escape into unmanaged code!
|
||||
Debug.Print(e.ToString());
|
||||
}
|
||||
|
||||
return @event;
|
||||
|
@ -234,8 +258,6 @@ namespace OpenTK.Platform.MacOS
|
|||
|
||||
NativeMethods.IOHIDManagerSetDeviceMatching(hidmanager, DeviceTypes.Ref);
|
||||
NativeMethods.IOHIDManagerOpen(hidmanager, IOOptionBits.Zero);
|
||||
|
||||
OpenTK.Platform.MacOS.Carbon.CF.CFRunLoopRunInMode(InputLoopMode, 0.0, true);
|
||||
}
|
||||
|
||||
void DeviceAdded(IntPtr context, IOReturn res, IntPtr sender, IOHIDDeviceRef device)
|
||||
|
@ -322,6 +344,7 @@ namespace OpenTK.Platform.MacOS
|
|||
{
|
||||
NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, IntPtr.Zero, IntPtr.Zero);
|
||||
NativeMethods.IOHIDDeviceUnscheduleFromRunLoop(device, RunLoop, InputLoopMode);
|
||||
NativeMethods.IOHIDDeviceClose(device, IOOptionBits.Zero);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@ -1032,7 +1055,11 @@ namespace OpenTK.Platform.MacOS
|
|||
|
||||
[DllImport(hid)]
|
||||
public static extern IOHIDManagerRef IOHIDManagerCreate(
|
||||
CFAllocatorRef allocator, IOOptionBits options) ;
|
||||
CFAllocatorRef allocator, IOOptionBits options);
|
||||
|
||||
[DllImport(hid)]
|
||||
public static extern IOReturn IOHIDManagerClose(
|
||||
IOHIDManagerRef allocator, IOOptionBits options);
|
||||
|
||||
// This routine will be called when a new (matching) device is connected.
|
||||
[DllImport(hid)]
|
||||
|
@ -1075,7 +1102,7 @@ namespace OpenTK.Platform.MacOS
|
|||
[DllImport(hid)]
|
||||
public static extern void IOHIDManagerSetDeviceMatching(
|
||||
IOHIDManagerRef manager,
|
||||
CFDictionaryRef matching) ;
|
||||
CFDictionaryRef matching);
|
||||
|
||||
[DllImport(hid)]
|
||||
public static extern IOReturn IOHIDManagerOpen(
|
||||
|
@ -1087,6 +1114,11 @@ namespace OpenTK.Platform.MacOS
|
|||
IOHIDDeviceRef manager,
|
||||
IOOptionBits opts);
|
||||
|
||||
[DllImport(hid)]
|
||||
public static extern IOReturn IOHIDDeviceClose(
|
||||
IOHIDDeviceRef device,
|
||||
IOOptionBits options);
|
||||
|
||||
[DllImport(hid)]
|
||||
public static extern CFTypeRef IOHIDDeviceGetProperty(
|
||||
IOHIDDeviceRef device,
|
||||
|
@ -1724,6 +1756,15 @@ namespace OpenTK.Platform.MacOS
|
|||
{
|
||||
if (manual)
|
||||
{
|
||||
if (MouseEventTapSource != IntPtr.Zero)
|
||||
{
|
||||
// Note: releasing the mach port (tap source)
|
||||
// automatically releases the event tap.
|
||||
CF.RunLoopRemoveSource(RunLoop, MouseEventTapSource, CF.RunLoopModeDefault);
|
||||
CF.CFRelease(MouseEventTapSource);
|
||||
MouseEventTapSource = IntPtr.Zero;
|
||||
}
|
||||
|
||||
NativeMethods.IOHIDManagerRegisterDeviceMatchingCallback(
|
||||
hidmanager, IntPtr.Zero, IntPtr.Zero);
|
||||
NativeMethods.IOHIDManagerRegisterDeviceRemovalCallback(
|
||||
|
@ -1746,19 +1787,15 @@ namespace OpenTK.Platform.MacOS
|
|||
DeviceRemoved(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, device);
|
||||
}
|
||||
|
||||
HandleDeviceAdded = null;
|
||||
HandleDeviceRemoved = null;
|
||||
HandleDeviceValueReceived = null;
|
||||
|
||||
if (MouseEventTap != IntPtr.Zero)
|
||||
if (hidmanager != IntPtr.Zero)
|
||||
{
|
||||
CF.CFRelease(MouseEventTap);
|
||||
MouseEventTap = IntPtr.Zero;
|
||||
NativeMethods.IOHIDManagerClose(hidmanager, IOOptionBits.Zero);
|
||||
hidmanager = IntPtr.Zero;
|
||||
}
|
||||
if (MouseEventTapSource != IntPtr.Zero)
|
||||
|
||||
if (RunLoop != IntPtr.Zero)
|
||||
{
|
||||
CF.CFRelease(MouseEventTapSource);
|
||||
MouseEventTapSource = IntPtr.Zero;
|
||||
CF.CFRelease(RunLoop);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -43,7 +43,13 @@ namespace OpenTK.Platform.MacOS
|
|||
internal const float ScrollFactor = 0.1f;
|
||||
internal static bool ExclusiveFullscreen = false;
|
||||
|
||||
readonly IInputDriver2 InputDriver = new HIDInput();
|
||||
readonly IInputDriver2 InputDriver;
|
||||
|
||||
public MacOSFactory()
|
||||
{
|
||||
NSApplication.Initialize();
|
||||
InputDriver = new HIDInput();
|
||||
}
|
||||
|
||||
#region IPlatformFactory Members
|
||||
|
||||
|
|
|
@ -112,6 +112,9 @@ namespace OpenTK.Platform.MacOS.Carbon
|
|||
[DllImport(appServices)]
|
||||
internal static extern IntPtr CFDictionaryGetValue(IntPtr theDictionary, IntPtr theKey);
|
||||
|
||||
[DllImport(appServices)]
|
||||
internal static extern IntPtr CFRetain(CFTypeRef cf);
|
||||
|
||||
[DllImport(appServices)]
|
||||
internal static extern void CFRelease(CFTypeRef cf);
|
||||
|
||||
|
@ -230,5 +233,11 @@ namespace OpenTK.Platform.MacOS.Carbon
|
|||
CFRunLoopRef rl,
|
||||
CFRunLoopSourceRef source,
|
||||
CFStringRef mode);
|
||||
|
||||
[DllImport(appServices, EntryPoint = "CFRunLoopRemoveSource")]
|
||||
internal static extern void RunLoopRemoveSource(
|
||||
CFRunLoopRef rl,
|
||||
CFRunLoopSourceRef source,
|
||||
CFStringRef mode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,22 @@ using System;
|
|||
|
||||
namespace OpenTK
|
||||
{
|
||||
/// <summary>Defines a plaftorm specific exception.</summary>
|
||||
/// <summary>
|
||||
/// Defines a plaftorm-specific exception.
|
||||
/// </summary>
|
||||
public class PlatformException : Exception
|
||||
{
|
||||
/// <summary>Constructs a new PlatformException.</summary>
|
||||
public PlatformException(string s) : base(s) { }
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OpenTK.PlatformException"/> class.
|
||||
/// </summary>
|
||||
public PlatformException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OpenTK.PlatformException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">A message explaining the cause for this exception.</param>
|
||||
public PlatformException(string message) : base(message) { }
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@
|
|||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Input;
|
||||
|
@ -42,6 +43,9 @@ namespace OpenTK.Platform
|
|||
/// </summary>
|
||||
abstract class PlatformFactoryBase : IPlatformFactory
|
||||
{
|
||||
static readonly object sync = new object();
|
||||
readonly List<IDisposable> Resources = new List<IDisposable>();
|
||||
|
||||
protected bool IsDisposed;
|
||||
|
||||
public PlatformFactoryBase()
|
||||
|
@ -80,6 +84,14 @@ namespace OpenTK.Platform
|
|||
return new LegacyJoystickDriver();
|
||||
}
|
||||
|
||||
public void RegisterResource(IDisposable resource)
|
||||
{
|
||||
lock (sync)
|
||||
{
|
||||
Resources.Add(resource);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable implementation
|
||||
|
@ -96,10 +108,19 @@ namespace OpenTK.Platform
|
|||
{
|
||||
if (manual)
|
||||
{
|
||||
lock (sync)
|
||||
{
|
||||
foreach (var resource in Resources)
|
||||
{
|
||||
resource.Dispose();
|
||||
}
|
||||
Resources.Clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Print("[OpenTK] {0} leaked, did you forget to call Dispose()?", GetType());
|
||||
Debug.Print("[OpenTK] {0} leaked with {1} live resources, did you forget to call Dispose()?",
|
||||
GetType().FullName, Resources.Count);
|
||||
}
|
||||
IsDisposed = true;
|
||||
}
|
||||
|
|
|
@ -376,7 +376,7 @@ namespace OpenTK.Platform.SDL2
|
|||
|
||||
#region IDisposable Members
|
||||
|
||||
void Dispose(bool manual)
|
||||
protected override void Dispose(bool manual)
|
||||
{
|
||||
if (!IsDisposed)
|
||||
{
|
||||
|
@ -397,17 +397,6 @@ namespace OpenTK.Platform.SDL2
|
|||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~Sdl2GraphicsContext()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -465,13 +465,7 @@ namespace OpenTK.Platform.Windows
|
|||
|
||||
#region IDisposable Members
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool calledManually)
|
||||
protected override void Dispose(bool calledManually)
|
||||
{
|
||||
if (!IsDisposed)
|
||||
{
|
||||
|
@ -479,20 +473,10 @@ namespace OpenTK.Platform.Windows
|
|||
{
|
||||
DestroyContext();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Print("[Warning] OpenGL context {0} leaked. Did you forget to call IGraphicsContext.Dispose()?",
|
||||
Handle.Handle);
|
||||
}
|
||||
IsDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
~WinGLContext()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
#region private void DestroyContext()
|
||||
|
||||
private void DestroyContext()
|
||||
|
|
|
@ -483,13 +483,7 @@ namespace OpenTK.Platform.X11
|
|||
|
||||
#region --- IDisposable Members ---
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
this.Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool manuallyCalled)
|
||||
protected override void Dispose(bool manuallyCalled)
|
||||
{
|
||||
if (!IsDisposed)
|
||||
{
|
||||
|
@ -516,12 +510,6 @@ namespace OpenTK.Platform.X11
|
|||
}
|
||||
IsDisposed = true;
|
||||
}
|
||||
|
||||
|
||||
~X11GLContext()
|
||||
{
|
||||
this.Dispose(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -178,22 +178,19 @@ namespace OpenTK
|
|||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Print("OpenTK.Toolkit leaked, did you forget to call Dispose()?");
|
||||
platform_factory = null;
|
||||
toolkit = null;
|
||||
initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
/// <summary>
|
||||
/// Finalizes this instance.
|
||||
/// </summary>
|
||||
~Toolkit()
|
||||
{
|
||||
Dispose(false);
|
||||
Debug.Print("[Warning] {0} leaked, did you forget to call Dispose()?");
|
||||
// We may not Dispose() the toolkit from the finalizer thread,
|
||||
// as that will crash on many operating systems.
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue