Improved resource disposal

Added IDisposable interface to all classes holding native data that must be freed. OpenTK.Toolkit.Init() now returns an IDisposable instance that can be used to cleanup all native data held by OpenTK. This is useful when re-initializing OpenTK (possibly in a new AppDomain), as is the case in the Example browser.
This commit is contained in:
Stefanos A 2013-10-01 22:01:27 +02:00
parent ff9cd61777
commit a85cecdc59
14 changed files with 515 additions and 153 deletions

View file

@ -32,7 +32,7 @@ using System.Text;
namespace OpenTK.Input
{
// Defines the interface for a 2nd generation input driver.
interface IInputDriver2
interface IInputDriver2 : IDisposable
{
IMouseDriver2 MouseDriver { get; }
IKeyboardDriver2 KeyboardDriver { get; }

View file

@ -27,6 +27,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace OpenTK.Platform
@ -37,6 +38,7 @@ namespace OpenTK.Platform
{
#region Fields
bool disposed;
static IPlatformFactory default_implementation, embedded_implementation;
#endregion
@ -134,6 +136,7 @@ namespace OpenTK.Platform
{
#region Fields
bool disposed;
static readonly string error_string = "Please, refer to http://www.opentk.com for more information.";
#endregion
@ -191,6 +194,72 @@ namespace OpenTK.Platform
}
#endregion
#region IDisposable Members
void Dispose(bool manual)
{
if (!disposed)
{
if (manual)
{
// nothing to do
}
else
{
Debug.Print("{0} leaked, did you forget to call Dispose()?", GetType());
}
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~UnsupportedPlatform()
{
Dispose(false);
}
#endregion
}
#endregion
#region IDisposable Members
void Dispose(bool manual)
{
if (!disposed)
{
if (manual)
{
Default.Dispose();
if (Embedded != Default)
{
Embedded.Dispose();
}
}
else
{
Debug.Print("{0} leaked, did you forget to call Dispose()?", GetType());
}
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~Factory()
{
Dispose(false);
}
#endregion

View file

@ -33,7 +33,7 @@ using OpenTK.Graphics;
namespace OpenTK.Platform
{
interface IPlatformFactory
interface IPlatformFactory : IDisposable
{
INativeWindow CreateNativeWindow(int x, int y, int width, int height, string title, GraphicsMode mode, GameWindowFlags options, DisplayDevice device);

View file

@ -120,28 +120,32 @@ namespace OpenTK.Platform.MacOS.Carbon
switch (evt.EventClass)
{
case EventClass.Application:
switch (evt.AppEventKind)
{
default:
return OSStatus.EventNotHandled;
}
case EventClass.Application:
switch (evt.AppEventKind)
{
case AppEventKind.AppQuit:
API.RemoveEventHandler(uppHandler);
return OSStatus.EventNotHandled;
default:
return OSStatus.EventNotHandled;
}
case EventClass.AppleEvent:
case EventClass.AppleEvent:
// only event here is the apple event.
Debug.Print("Processing apple event.");
API.ProcessAppleEvent(inEvent);
break;
Debug.Print("Processing apple event.");
API.ProcessAppleEvent(inEvent);
break;
case EventClass.Keyboard:
case EventClass.Mouse:
if (WindowEventHandler != null)
{
return WindowEventHandler.DispatchEvent(inCaller, inEvent, evt, userData);
}
case EventClass.Keyboard:
case EventClass.Mouse:
if (WindowEventHandler != null)
{
return WindowEventHandler.DispatchEvent(inCaller, inEvent, evt, userData);
}
break;
break;
}
return OSStatus.EventNotHandled;

View file

@ -175,8 +175,8 @@ namespace OpenTK.Platform.MacOS
{
if (uppHandler != IntPtr.Zero)
{
//API.RemoveEventHandler(uppHandler);
//API.DisposeEventHandlerUPP(uppHandler);
API.RemoveEventHandler(uppHandler);
API.DisposeEventHandlerUPP(uppHandler);
}
uppHandler = IntPtr.Zero;
@ -925,8 +925,6 @@ namespace OpenTK.Platform.MacOS
return;
OnClosed();
Dispose();
}
public WindowState WindowState

View file

@ -69,9 +69,11 @@ namespace OpenTK.Platform.MacOS
readonly CFString InputLoopMode = CF.RunLoopModeDefault;
readonly CFDictionary DeviceTypes = new CFDictionary();
readonly NativeMethods.IOHIDDeviceCallback HandleDeviceAdded;
readonly NativeMethods.IOHIDDeviceCallback HandleDeviceRemoved;
readonly NativeMethods.IOHIDValueCallback HandleDeviceValueReceived;
NativeMethods.IOHIDDeviceCallback HandleDeviceAdded;
NativeMethods.IOHIDDeviceCallback HandleDeviceRemoved;
NativeMethods.IOHIDValueCallback HandleDeviceValueReceived;
bool disposed;
#endregion
@ -164,6 +166,7 @@ namespace OpenTK.Platform.MacOS
// Thanks to Jase: http://www.opentk.com/node/2800
NativeMethods.IOHIDDeviceRegisterInputValueCallback(device,
HandleDeviceValueReceived, device);
NativeMethods.IOHIDDeviceScheduleWithRunLoop(device, RunLoop, InputLoopMode);
}
}
@ -192,12 +195,19 @@ namespace OpenTK.Platform.MacOS
KeyboardDevices[device] = state;
}
NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, null, IntPtr.Zero);
NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, IntPtr.Zero, IntPtr.Zero);
NativeMethods.IOHIDDeviceUnscheduleWithRunLoop(device, RunLoop, InputLoopMode);
}
void DeviceValueReceived(IntPtr context, IOReturn res, IntPtr sender, IOHIDValueRef val)
{
if (disposed)
{
Debug.Print("DeviceValueReceived({0}, {1}, {2}, {3}) called on disposed {4}",
context, res, sender, val, GetType());
return;
}
MouseState mouse;
KeyboardState keyboard;
if (MouseDevices.TryGetValue(context, out mouse))
@ -383,6 +393,12 @@ namespace OpenTK.Platform.MacOS
IOHIDDeviceCallback inIOHIDDeviceCallback,
IntPtr inContext);
[DllImport(hid)]
public static extern void IOHIDManagerRegisterDeviceMatchingCallback(
IOHIDManagerRef inIOHIDManagerRef,
IntPtr inIOHIDDeviceCallback,
IntPtr inContext);
// This routine will be called when a (matching) device is disconnected.
[DllImport(hid)]
public static extern void IOHIDManagerRegisterDeviceRemovalCallback(
@ -390,6 +406,12 @@ namespace OpenTK.Platform.MacOS
IOHIDDeviceCallback inIOHIDDeviceCallback,
IntPtr inContext);
[DllImport(hid)]
public static extern void IOHIDManagerRegisterDeviceRemovalCallback(
IOHIDManagerRef inIOHIDManagerRef,
IntPtr inIOHIDDeviceCallback,
IntPtr inContext);
[DllImport(hid)]
public static extern void IOHIDManagerScheduleWithRunLoop(
IOHIDManagerRef inIOHIDManagerRef,
@ -428,6 +450,12 @@ namespace OpenTK.Platform.MacOS
IOHIDValueCallback callback,
IntPtr context);
[DllImport(hid)]
public static extern void IOHIDDeviceRegisterInputValueCallback(
IOHIDDeviceRef device,
IntPtr callback,
IntPtr context);
[DllImport(hid)]
public static extern void IOHIDDeviceScheduleWithRunLoop(
IOHIDDeviceRef device,
@ -939,6 +967,59 @@ namespace OpenTK.Platform.MacOS
};
#endregion
#region IDisposable Members
void Dispose(bool manual)
{
if (!disposed)
{
if (manual)
{
NativeMethods.IOHIDManagerRegisterDeviceMatchingCallback(
hidmanager, IntPtr.Zero, IntPtr.Zero);
NativeMethods.IOHIDManagerRegisterDeviceRemovalCallback(
hidmanager, IntPtr.Zero, IntPtr.Zero);
NativeMethods.IOHIDManagerScheduleWithRunLoop(
hidmanager, IntPtr.Zero, IntPtr.Zero);
foreach (var device in MouseDevices.Keys)
{
NativeMethods.IOHIDDeviceRegisterInputValueCallback(
device, IntPtr.Zero, IntPtr.Zero);
}
foreach (var device in KeyboardDevices.Keys)
{
NativeMethods.IOHIDDeviceRegisterInputValueCallback(
device, IntPtr.Zero, IntPtr.Zero);
}
HandleDeviceAdded = null;
HandleDeviceRemoved = null;
HandleDeviceValueReceived = null;
}
else
{
Debug.Print("{0} leaked, did you forget to call Dispose()?", GetType());
}
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~HIDInput()
{
Dispose(false);
}
#endregion
}
}

View file

@ -27,6 +27,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using OpenTK.Input;
@ -38,6 +39,7 @@ namespace OpenTK.Platform.MacOS
{
#region Fields
bool disposed;
readonly IInputDriver2 InputDriver = new HIDInput();
#endregion
@ -93,5 +95,36 @@ namespace OpenTK.Platform.MacOS
}
#endregion
#region IDisposable Members
void Dispose(bool manual)
{
if (!disposed)
{
if (manual)
{
InputDriver.Dispose();
}
else
{
Debug.Print("{0} leaked, did you forget to call Dispose()?", GetType());
}
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~MacOSFactory()
{
Dispose(false);
}
#endregion
}
}

View file

@ -34,6 +34,7 @@ namespace OpenTK.Platform.SDL2
class Sdl2Factory : IPlatformFactory
{
readonly IInputDriver2 InputDriver = new Sdl2InputDriver();
bool disposed;
public Sdl2Factory()
{
@ -102,6 +103,33 @@ namespace OpenTK.Platform.SDL2
}
#endregion
#region IDisposable Members
void Dispose(bool manual)
{
if (!disposed)
{
if (manual)
{
InputDriver.Dispose();
}
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~Sdl2Factory()
{
Dispose(false);
}
#endregion
}
}

View file

@ -27,6 +27,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using OpenTK.Input;
@ -38,6 +39,7 @@ namespace OpenTK.Platform.SDL2
readonly Sdl2Keyboard keyboard_driver = new Sdl2Keyboard();
readonly Sdl2Mouse mouse_driver = new Sdl2Mouse();
readonly SDL.SDL_EventFilter EventFilterDelegate;
bool disposed;
public Sdl2InputDriver()
{
@ -109,6 +111,37 @@ namespace OpenTK.Platform.SDL2
}
#endregion
#region IDisposable Members
void Dispose(bool manual)
{
if (!disposed)
{
if (manual)
{
SDL.SDL_DelEventWatch(EventFilterDelegate, IntPtr.Zero);
}
else
{
Debug.WriteLine("Sdl2InputDriver leaked, did you forget to call Dispose()?");
}
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~Sdl2InputDriver()
{
Dispose(false);
}
#endregion
}
}

View file

@ -49,6 +49,7 @@ namespace OpenTK.Platform.SDL2
WindowState previous_window_state = WindowState.Normal;
WindowBorder window_border = WindowBorder.Resizable;
Icon icon;
string window_title;
KeyboardDevice keyboard = new KeyboardDevice();
MouseDevice mouse = new MouseDevice();
@ -84,6 +85,7 @@ namespace OpenTK.Platform.SDL2
window = new Sdl2WindowInfo(handle, null);
window_id = SDL.SDL_GetWindowID(handle);
windows.Add(window_id, this);
window_title = title;
keyboard.Description = "Standard Windows keyboard";
keyboard.NumberOfFunctionKeys = 12;
@ -227,6 +229,7 @@ namespace OpenTK.Platform.SDL2
{
exists = false;
SDL.SDL_DelEventWatch(EventFilterDelegate, IntPtr.Zero);
if (windows.ContainsKey(window_id))
{
windows.Remove(window_id);
@ -245,10 +248,10 @@ namespace OpenTK.Platform.SDL2
SDL.SDL_SetRelativeMouseMode(
grab ? SDL.SDL_bool.SDL_TRUE : SDL.SDL_bool.SDL_FALSE);
if (grab)
{
mouse.Position = new Point(0, 0);
}
//if (grab)
//{
// mouse.Position = new Point(0, 0);
//}
}
// Hack to force WindowState events to be pumped
@ -458,6 +461,7 @@ namespace OpenTK.Platform.SDL2
set
{
SDL.SDL_SetWindowTitle(window.Handle, value);
window_title = value;
}
}

View file

@ -49,10 +49,11 @@ namespace OpenTK.Platform.Windows
readonly object MouseLock = new object();
readonly object KeyboardLock = new object();
readonly WinMMJoystick gamepad_driver = new WinMMJoystick();
readonly WinKeyMap KeyMap = new WinKeyMap();
KeyboardState keyboard = new KeyboardState();
MouseState mouse = new MouseState();
readonly WinKeyMap KeyMap = new WinKeyMap();
bool disposed;
#endregion
@ -173,5 +174,36 @@ namespace OpenTK.Platform.Windows
}
#endregion
#region IDisposable Members
void Dispose(bool manual)
{
if (!disposed)
{
if (manual)
{
// Todo: implement this
}
else
{
Debug.Print("{0} leaked, did you forget to call Dispose()?", GetType());
}
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~WMInput()
{
Dispose(false);
}
#endregion
}
}

View file

@ -37,6 +37,7 @@ namespace OpenTK.Platform.Windows
class WinFactory : IPlatformFactory
{
bool disposed;
readonly object SyncRoot = new object();
IInputDriver2 inputDriver;
@ -123,5 +124,36 @@ namespace OpenTK.Platform.Windows
}
}
}
#region IDisposable Members
void Dispose(bool manual)
{
if (!disposed)
{
if (manual)
{
InputDriver.Dispose();
}
else
{
Debug.Print("{0} leaked, did you forget to call Dispose()?", GetType());
}
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~WinFactory()
{
Dispose(false);
}
#endregion
}
}

View file

@ -33,6 +33,8 @@ namespace OpenTK.Platform.X11
{
class X11Factory : IPlatformFactory
{
bool disposed;
#region Constructors
public X11Factory()
@ -97,5 +99,36 @@ namespace OpenTK.Platform.X11
}
#endregion
#region IDisposable Members
void Dispose(bool manual)
{
if (!disposed)
{
if (manual)
{
// nothing to do
}
else
{
Debug.Print("{0} leaked, did you forget to call Dispose()?", GetType());
}
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~X11Factory()
{
Dispose(false);
}
#endregion
}
}

View file

@ -28,6 +28,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using OpenTK.Platform;
namespace OpenTK
{
@ -36,6 +37,8 @@ namespace OpenTK
/// </summary>
public sealed class Toolkit
{
static Factory platform_factory;
volatile static bool initialized;
static readonly object InitLock = new object();
@ -59,7 +62,7 @@ namespace OpenTK
/// Calling this method first ensures that OpenTK is given the chance to
/// initialize itself and configure the platform correctly.
/// </remarks>
public static void Init()
public static IDisposable Init()
{
lock (InitLock)
{
@ -69,11 +72,23 @@ namespace OpenTK
Configuration.Init();
// The actual initialization takes place in the platform-specific factory
// constructors.
new Platform.Factory();
platform_factory = new Platform.Factory();
AppDomain.CurrentDomain.DomainUnload += Deinit;
}
return platform_factory;
}
}
#endregion
#region Private Members
static void Deinit(object sender, EventArgs e)
{
AppDomain.CurrentDomain.DomainUnload -= Deinit;
platform_factory.Dispose();
}
#endregion
}
}