From 40cfcfaf25f522ff0c53e1cc0ab0092a7926c077 Mon Sep 17 00:00:00 2001 From: Stefanos A Date: Wed, 2 Oct 2013 01:14:26 +0200 Subject: [PATCH] Improved diagnostics in Dispose() Leaked and disposed resources are now always reported. --- Source/OpenTK/Platform/SDL2/Sdl2Factory.cs | 6 + .../Platform/SDL2/Sdl2GraphicsContext.cs | 19 +- .../OpenTK/Platform/SDL2/Sdl2InputDriver.cs | 75 +++-- .../OpenTK/Platform/SDL2/Sdl2NativeWindow.cs | 309 +++++++++++------- Source/OpenTK/Platform/SDL2/Sdl2WindowInfo.cs | 2 + 5 files changed, 252 insertions(+), 159 deletions(-) diff --git a/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs b/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs index ee569a06..18d62ac8 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2Factory.cs @@ -26,6 +26,7 @@ #endregion using System; +using System.Diagnostics; using OpenTK.Graphics; using OpenTK.Input; @@ -112,8 +113,13 @@ namespace OpenTK.Platform.SDL2 { if (manual) { + Debug.Print("Disposing {0}", GetType()); InputDriver.Dispose(); } + else + { + Debug.WriteLine("Sdl2Factory leaked, did you forget to call Dispose()?"); + } disposed = true; } } diff --git a/Source/OpenTK/Platform/SDL2/Sdl2GraphicsContext.cs b/Source/OpenTK/Platform/SDL2/Sdl2GraphicsContext.cs index d004f93e..dedc3f0d 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2GraphicsContext.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2GraphicsContext.cs @@ -37,7 +37,7 @@ namespace OpenTK.Platform.SDL2 IWindowInfo Window { get; set; } ContextHandle SdlContext { get; set; } - public Sdl2GraphicsContext(IWindowInfo window) + Sdl2GraphicsContext(IWindowInfo window) { // It is possible to create a GraphicsContext on a window // that is not owned by SDL (e.g. a GLControl). In that case, @@ -61,8 +61,11 @@ namespace OpenTK.Platform.SDL2 OpenTK.Graphics.GraphicsContextFlags flags) : this(win) { - SetGLAttributes(mode, shareContext, major, minor, flags); - SdlContext = new ContextHandle(SDL.SDL_GL_CreateContext(Window.Handle)); + lock (SDL.Sync) + { + SetGLAttributes(mode, shareContext, major, minor, flags); + SdlContext = new ContextHandle(SDL.SDL_GL_CreateContext(Window.Handle)); + } if (SdlContext == ContextHandle.Zero) { Debug.Print("SDL2 failed to create OpenGL context: {0}", SDL.SDL_GetError()); @@ -218,15 +221,19 @@ namespace OpenTK.Platform.SDL2 void Dispose(bool manual) { - if (IsDisposed) + if (!IsDisposed) { if (manual) { - SDL.SDL_GL_DeleteContext(SdlContext.Handle); + Debug.Print("Disposing {0}", GetType()); + lock (SDL.Sync) + { + SDL.SDL_GL_DeleteContext(SdlContext.Handle); + } } else { - Debug.Print("[Warning] IGraphicsContext leaked ({0}). Did you forget to call IGraphicsContext.Dispose()?", this); + Debug.WriteLine("Sdl2GraphicsContext leaked, did you forget to call Dispose()?"); } IsDisposed = true; } diff --git a/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs b/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs index c8c1d368..eb67e4c0 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2InputDriver.cs @@ -36,47 +36,62 @@ namespace OpenTK.Platform.SDL2 { class Sdl2InputDriver : IInputDriver2 { + readonly static Dictionary DriverHandles = + new Dictionary(); + readonly Sdl2Keyboard keyboard_driver = new Sdl2Keyboard(); readonly Sdl2Mouse mouse_driver = new Sdl2Mouse(); - readonly SDL.SDL_EventFilter EventFilterDelegate; + readonly SDL.SDL_EventFilter EventFilterDelegate = FilterInputEvents; + + static int count; bool disposed; public Sdl2InputDriver() { - EventFilterDelegate = FilterInputEvents; - SDL.SDL_AddEventWatch(EventFilterDelegate, IntPtr.Zero); + lock (SDL.Sync) + { + IntPtr driver_handle = new IntPtr(count++); + DriverHandles.Add(driver_handle, this); + SDL.SDL_AddEventWatch(EventFilterDelegate, driver_handle); + } } #region Private Members - int FilterInputEvents(IntPtr user_data, IntPtr e) + unsafe static int FilterInputEvents(IntPtr driver_handle, IntPtr e) { - SDL.SDL_Event ev; - unsafe + try { - ev = *(SDL.SDL_Event*)e; + SDL.SDL_Event ev = *(SDL.SDL_Event*)e; + + Sdl2InputDriver driver; + if (DriverHandles.TryGetValue(driver_handle, out driver)) + { + switch (ev.type) + { + case SDL.SDL_EventType.SDL_KEYDOWN: + case SDL.SDL_EventType.SDL_KEYUP: + driver.keyboard_driver.ProcessKeyboardEvent(ev.key); + break; + + case SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN: + case SDL.SDL_EventType.SDL_MOUSEBUTTONUP: + driver.mouse_driver.ProcessMouseEvent(ev.button); + break; + + case SDL.SDL_EventType.SDL_MOUSEMOTION: + driver.mouse_driver.ProcessMouseEvent(ev.motion); + break; + + case SDL.SDL_EventType.SDL_MOUSEWHEEL: + driver.mouse_driver.ProcessWheelEvent(ev.wheel); + break; + } + } } - - var type = (SDL.SDL_EventType)Marshal.ReadInt32(e); - switch (type) + catch (Exception ex) { - case SDL.SDL_EventType.SDL_KEYDOWN: - case SDL.SDL_EventType.SDL_KEYUP: - keyboard_driver.ProcessKeyboardEvent(ev.key); - break; - - case SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN: - case SDL.SDL_EventType.SDL_MOUSEBUTTONUP: - mouse_driver.ProcessMouseEvent(ev.button); - break; - - case SDL.SDL_EventType.SDL_MOUSEMOTION: - mouse_driver.ProcessMouseEvent(ev.motion); - break; - - case SDL.SDL_EventType.SDL_MOUSEWHEEL: - mouse_driver.ProcessWheelEvent(ev.wheel); - break; + Debug.Print(ex.ToString()); } return 0; @@ -120,7 +135,11 @@ namespace OpenTK.Platform.SDL2 { if (manual) { - SDL.SDL_DelEventWatch(EventFilterDelegate, IntPtr.Zero); + Debug.Print("Disposing {0}", GetType()); + lock (SDL.Sync) + { + SDL.SDL_DelEventWatch(EventFilterDelegate, IntPtr.Zero); + } } else { diff --git a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs index 7588073c..39e0c22d 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2NativeWindow.cs @@ -56,19 +56,13 @@ namespace OpenTK.Platform.SDL2 IList keyboards = new List(1); IList mice = new List(1); - static readonly SDL.SDL_EventFilter EventFilterDelegate = FilterEvents; + readonly SDL.SDL_EventFilter EventFilterDelegate = FilterEvents; static readonly Dictionary windows = new Dictionary(); static readonly Sdl2KeyMap map = new Sdl2KeyMap(); - static Sdl2NativeWindow() - { - // store the filter delegate to protect it from the GC - SDL.SDL_AddEventWatch(EventFilterDelegate, IntPtr.Zero); - } - public Sdl2NativeWindow(int x, int y, int width, int height, string title, GameWindowFlags options, DisplayDevice device) { @@ -81,7 +75,11 @@ namespace OpenTK.Platform.SDL2 (flags & SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN) != 0) window_state = WindowState.Fullscreen; - IntPtr handle = SDL.SDL_CreateWindow(title, bounds.Left + x, bounds.Top + y, width, height, flags); + IntPtr handle; + lock (SDL.Sync) + { + handle = SDL.SDL_CreateWindow(title, bounds.Left + x, bounds.Top + y, width, height, flags); + } window = new Sdl2WindowInfo(handle, null); window_id = SDL.SDL_GetWindowID(handle); windows.Add(window_id, this); @@ -99,6 +97,11 @@ namespace OpenTK.Platform.SDL2 keyboards.Add(keyboard); mice.Add(mouse); + lock (SDL.Sync) + { + SDL.SDL_AddEventWatch(EventFilterDelegate, handle); + } + exists = true; } @@ -132,41 +135,151 @@ namespace OpenTK.Platform.SDL2 return TranslateKey(scan); } - static int FilterEvents(IntPtr user_data, IntPtr e) + unsafe static int FilterEvents(IntPtr user_data, IntPtr e) { - var type = (SDL.SDL_EventType)Marshal.ReadInt32(e); - if (type == SDL.SDL_EventType.SDL_WINDOWEVENT) - { - unsafe - { - SDL.SDL_Event ev = *(SDL.SDL_Event*)e; + bool processed = false; - // Dispatch this event to the correct window - Sdl2NativeWindow window; + try + { + + Sdl2NativeWindow window = null; + SDL.SDL_Event ev = *(SDL.SDL_Event*)e; + + switch (ev.type) + { + case SDL.SDL_EventType.SDL_WINDOWEVENT: if (windows.TryGetValue(ev.window.windowID, out window)) { - window.ProcessWindowEvent(ev.window); + ProcessWindowEvent(window, ev.window); + processed = true; } - } - return 0; // event processed, drop from queue + break; + + case SDL.SDL_EventType.SDL_KEYDOWN: + case SDL.SDL_EventType.SDL_KEYUP: + if (windows.TryGetValue(ev.key.windowID, out window)) + { + ProcessKeyEvent(window, ev); + processed = true; + } + break; + + case SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN: + case SDL.SDL_EventType.SDL_MOUSEBUTTONUP: + if (windows.TryGetValue(ev.button.windowID, out window)) + { + ProcessButtonEvent(window, ev); + processed = true; + } + break; + + case SDL.SDL_EventType.SDL_MOUSEMOTION: + if (windows.TryGetValue(ev.motion.windowID, out window)) + { + ProcessMotionEvent(window, ev); + processed = true; + } + break; + + case SDL.SDL_EventType.SDL_MOUSEWHEEL: + if (windows.TryGetValue(ev.wheel.windowID, out window)) + { + ProcessWheelEvent(window, ev); + processed = true; + } + break; + + case SDL.SDL_EventType.SDL_QUIT: + /* + if (windows.TryGetValue(ev.quit.windowID, out window)) + { + + } + */ + break; } - return 1; // event not processed, add to queue + } + catch (Exception ex) + { + Debug.Print(ex.ToString()); + } + + return processed ? 0 : 1; } - void ProcessWindowEvent(SDL.SDL_WindowEvent e) + static void ProcessButtonEvent(Sdl2NativeWindow window, SDL.SDL_Event ev) + { + bool button_pressed = ev.button.state == SDL.SDL_PRESSED; + switch (ev.button.button) + { + case (byte)SDL.SDL_BUTTON_LEFT: + window.mouse[MouseButton.Left] = button_pressed; + break; + + case (byte)SDL.SDL_BUTTON_MIDDLE: + window.mouse[MouseButton.Middle] = button_pressed; + break; + + case (byte)SDL.SDL_BUTTON_RIGHT: + window.mouse[MouseButton.Right] = button_pressed; + break; + + case (byte)SDL.SDL_BUTTON_X1: + window.mouse[MouseButton.Button1] = button_pressed; + break; + + case (byte)SDL.SDL_BUTTON_X2: + window.mouse[MouseButton.Button2] = button_pressed; + break; + } + } + + static void ProcessKeyEvent(Sdl2NativeWindow window, SDL.SDL_Event ev) + { + bool key_pressed = ev.key.state == SDL.SDL_PRESSED; + var key = ev.key.keysym; + window.keyboard.SetKey(TranslateKey(key.scancode), (uint)key.scancode, key_pressed); + } + + static void ProcessMotionEvent(Sdl2NativeWindow window, SDL.SDL_Event ev) + { + if (window.CursorVisible) + { + window.mouse.Position = new Point(ev.motion.x, ev.motion.y); + } + else + { + window.mouse.Position = new Point( + window.mouse.X + ev.motion.xrel, + window.mouse.Y + ev.motion.yrel); + } + } + + static void ProcessWheelEvent(Sdl2NativeWindow window, SDL.SDL_Event ev) + { + window.mouse.Wheel += ev.wheel.y; + } + + static void ProcessWindowEvent(Sdl2NativeWindow window, SDL.SDL_WindowEvent e) { switch (e.windowEvent) { case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_CLOSE: - Closed(this, EventArgs.Empty); + var close_args = new System.ComponentModel.CancelEventArgs(); + window.Closing(window, close_args); + if (!close_args.Cancel) + { + window.Closed(window, EventArgs.Empty); + window.DestroyWindow(); + } break; case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_ENTER: - MouseEnter(this, EventArgs.Empty); + window.MouseEnter(window, EventArgs.Empty); break; case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_LEAVE: - MouseLeave(this, EventArgs.Empty); + window.MouseLeave(window, EventArgs.Empty); break; case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_EXPOSED: @@ -174,49 +287,49 @@ namespace OpenTK.Platform.SDL2 break; case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_GAINED: - is_focused = true; - FocusedChanged(this, EventArgs.Empty); + window.is_focused = true; + window.FocusedChanged(window, EventArgs.Empty); break; case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_LOST: - is_focused = false; - FocusedChanged(this, EventArgs.Empty); + window.is_focused = false; + window.FocusedChanged(window, EventArgs.Empty); break; case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_HIDDEN: - is_visible = false; - VisibleChanged(this, EventArgs.Empty); + window.is_visible = false; + window.VisibleChanged(window, EventArgs.Empty); break; case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_SHOWN: - is_visible = true; - VisibleChanged(this, EventArgs.Empty); + window.is_visible = true; + window.VisibleChanged(window, EventArgs.Empty); break; case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_MAXIMIZED: - previous_window_state = window_state; - window_state = OpenTK.WindowState.Maximized; - WindowStateChanged(this, EventArgs.Empty); + window.previous_window_state = window.window_state; + window.window_state = OpenTK.WindowState.Maximized; + window.WindowStateChanged(window, EventArgs.Empty); break; case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_MINIMIZED: - previous_window_state = window_state; - window_state = OpenTK.WindowState.Minimized; - WindowStateChanged(this, EventArgs.Empty); + window.previous_window_state = window.window_state; + window.window_state = OpenTK.WindowState.Minimized; + window.WindowStateChanged(window, EventArgs.Empty); break; case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_RESTORED: - window_state = previous_window_state; - WindowStateChanged(this, EventArgs.Empty); + window.window_state = window.previous_window_state; + window.WindowStateChanged(window, EventArgs.Empty); break; case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_MOVED: - Move(this, EventArgs.Empty); + window.Move(window, EventArgs.Empty); break; case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_RESIZED: case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_SIZE_CHANGED: - Resize(this, EventArgs.Empty); + window.Resize(window, EventArgs.Empty); break; default: @@ -229,12 +342,18 @@ namespace OpenTK.Platform.SDL2 { exists = false; - SDL.SDL_DelEventWatch(EventFilterDelegate, IntPtr.Zero); - if (windows.ContainsKey(window_id)) + if (window.Handle != IntPtr.Zero) { - windows.Remove(window_id); + lock (SDL.Sync) + { + SDL.SDL_DestroyWindow(window.Handle); + SDL.SDL_DelEventWatch(EventFilterDelegate, window.Handle); + if (windows.ContainsKey(window_id)) + { + windows.Remove(window_id); + } + } } - SDL.SDL_DestroyWindow(window.Handle); window_id = 0; window.Handle = IntPtr.Zero; @@ -247,20 +366,15 @@ namespace OpenTK.Platform.SDL2 grab ? SDL.SDL_bool.SDL_TRUE : SDL.SDL_bool.SDL_FALSE); SDL.SDL_SetRelativeMouseMode( grab ? SDL.SDL_bool.SDL_TRUE : SDL.SDL_bool.SDL_FALSE); - - //if (grab) - //{ - // mouse.Position = new Point(0, 0); - //} } // Hack to force WindowState events to be pumped void HideShowWindowHack() { SDL.SDL_HideWindow(window.Handle); - SDL.SDL_PumpEvents(); + ProcessEvents(); SDL.SDL_ShowWindow(window.Handle); - SDL.SDL_PumpEvents(); + ProcessEvents(); } // Revert to WindowState.Normal if necessary @@ -284,7 +398,7 @@ namespace OpenTK.Platform.SDL2 break; } - SDL.SDL_PumpEvents(); + ProcessEvents(); window_state = WindowState.Normal; } @@ -316,80 +430,24 @@ namespace OpenTK.Platform.SDL2 { Debug.Print("SDL2 destroying window {0}", window.Handle); SDL.SDL_Event e = new SDL.SDL_Event(); - e.type = SDL.SDL_EventType.SDL_QUIT; - SDL.SDL_PushEvent(ref e); - SDL.SDL_PumpEvents(); + //e.type = SDL.SDL_EventType.SDL_QUIT; + e.type = SDL.SDL_EventType.SDL_WINDOWEVENT; + e.window.windowEvent = SDL.SDL_WindowEventID.SDL_WINDOWEVENT_CLOSE; + e.window.windowID = window_id; + lock (SDL.Sync) + { + SDL.SDL_PushEvent(ref e); + } } } public void ProcessEvents() { - SDL.SDL_Event e; - while (SDL.SDL_PollEvent(out e) != 0) + if (Exists) { - switch (e.type) + lock (SDL.Sync) { - case SDL.SDL_EventType.SDL_KEYDOWN: - case SDL.SDL_EventType.SDL_KEYUP: - bool key_pressed = e.key.state == SDL.SDL_PRESSED; - var key = e.key.keysym; - keyboard.SetKey(TranslateKey(key.scancode), (uint)key.scancode, key_pressed); - break; - - case SDL.SDL_EventType.SDL_MOUSEBUTTONDOWN: - case SDL.SDL_EventType.SDL_MOUSEBUTTONUP: - bool button_pressed = e.button.state == SDL.SDL_PRESSED; - switch (e.button.button) - { - case (byte)SDL.SDL_BUTTON_LEFT: - mouse[MouseButton.Left] = button_pressed; - break; - - case (byte)SDL.SDL_BUTTON_MIDDLE: - mouse[MouseButton.Middle] = button_pressed; - break; - - case (byte)SDL.SDL_BUTTON_RIGHT: - mouse[MouseButton.Right] = button_pressed; - break; - - case (byte)SDL.SDL_BUTTON_X1: - mouse[MouseButton.Button1] = button_pressed; - break; - - case (byte)SDL.SDL_BUTTON_X2: - mouse[MouseButton.Button2] = button_pressed; - break; - } - break; - - case SDL.SDL_EventType.SDL_MOUSEMOTION: - if (CursorVisible) - { - mouse.Position = new Point(e.motion.x, e.motion.y); - } - else - { - mouse.Position = new Point(mouse.X + e.motion.xrel, mouse.Y + e.motion.yrel); - } - break; - - case SDL.SDL_EventType.SDL_MOUSEWHEEL: - mouse.Wheel += e.wheel.y; - break; - - case SDL.SDL_EventType.SDL_QUIT: - var close_args = new System.ComponentModel.CancelEventArgs(); - Closing(this, close_args); - if (!close_args.Cancel) - { - DestroyWindow(); - } - break; - - case SDL.SDL_EventType.SDL_WINDOWEVENT: - // do nothing (this is processed in the event filter) - break; + SDL.SDL_PumpEvents(); } } } @@ -776,10 +834,11 @@ namespace OpenTK.Platform.SDL2 void Dispose(bool manual) { - if (manual) + if (!disposed) { - if (!disposed) + if (manual) { + Debug.Print("Disposing {0}", GetType()); if (Exists) { DestroyWindow(); @@ -787,7 +846,7 @@ namespace OpenTK.Platform.SDL2 } else { - Debug.Print("[Warning] INativeWindow leaked ({0}). Did you forget to call INativeWindow.Dispose()?", this); + Debug.WriteLine("Sdl2NativeWindow leaked, did you forget to call Dispose()?"); } disposed = true; } diff --git a/Source/OpenTK/Platform/SDL2/Sdl2WindowInfo.cs b/Source/OpenTK/Platform/SDL2/Sdl2WindowInfo.cs index 0ddeec61..fef67067 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2WindowInfo.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2WindowInfo.cs @@ -26,6 +26,7 @@ #endregion using System; +using System.Diagnostics; namespace OpenTK.Platform.SDL2 { @@ -48,6 +49,7 @@ namespace OpenTK.Platform.SDL2 public void Dispose() { + Debug.Print("Disposing {0}", GetType()); } #endregion