diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index b2ac53b5..640e95f7 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -17,6 +17,7 @@ using OpenTK.Input; using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL.Enums; using OpenTK.Graphics; +using System.ComponentModel; namespace OpenTK { @@ -66,7 +67,7 @@ namespace OpenTK bool disposed; double update_period, render_period; - double target_update_period, target_render_period, target_render_period_doubled; + double target_update_period, target_render_period; // TODO: Implement these: double update_time, render_time;//, event_time; //bool allow_sleep = true; // If true, GameWindow will call Timer.Sleep() if there is enough time. @@ -77,6 +78,9 @@ namespace OpenTK IGraphicsContext glContext; + int main_loop_thread_id; + object exit_lock = new object(); + #endregion #region --- Contructors --- @@ -221,29 +225,34 @@ namespace OpenTK void glWindow_Destroy(object sender, EventArgs e) { glWindow.Destroy -= glWindow_Destroy; - this.Exit(); + ExitAsync(); } #endregion #region void ExitInternal() - /// - /// Stops the main loop. + // Stops the main loop, if one exists. void ExitInternal() { - //Debug.Print("Firing GameWindowExitException"); if (HasMainLoop) { throw new GameWindowExitException(); } - if (CloseWindow != null) - { - CloseWindow(this, EventArgs.Empty); - } } - public event EventHandler CloseWindow; + #region void ExitAsync() + + // Gracefully exits the GameWindow. May be called from any thread. + void ExitAsync() + { + if (disposed) + throw new ObjectDisposedException("GameWindow"); + + UpdateFrame += CallExitInternal; + } + + // Used in ExitAsync() to ensure ExitInternal() is called from the main thread. void CallExitInternal(GameWindow sender, UpdateFrameEventArgs e) { UpdateFrame -= CallExitInternal; @@ -252,6 +261,8 @@ namespace OpenTK #endregion + #endregion + #region bool MustResize bool MustResize @@ -277,45 +288,36 @@ namespace OpenTK #region public virtual void Exit() - /// - /// Gracefully exits the GameWindow. May only be called from the thread where the GameWindow was created. - /// - /// - /// Override if you want to provide yor own exit sequence. - /// If you override this method, place a call to base.Exit(), to ensure - /// proper OpenTK shutdown. - /// - public virtual void Exit() - { - if (disposed) throw new ObjectDisposedException("GameWindow"); - //glWindow.DestroyWindow(); - //while (glWindow.Exists) - // glWindow.ProcessEvents(); - if (HasMainLoop) - ExitAsync(); - else - ExitInternal(); - //isExiting = true; - //UpdateFrame += CallExitInternal; - } - - #endregion - - #region public virtual void ExitAsync() - /// /// Gracefully exits the GameWindow. May be called from any thread. /// /// - /// Override if you want to provide yor own exit sequence. - /// If you override this method, place a call to base.ExitAsync(), to ensure - /// proper OpenTK shutdown. + /// Override if you are not using . + /// If you override this method, place a call to base.Exit(), to ensure proper OpenTK shutdown. /// - public virtual void ExitAsync() + public virtual void Exit() { - //isExiting = true; - if (disposed) throw new ObjectDisposedException("GameWindow"); - UpdateFrame += CallExitInternal; + lock (exit_lock) + { + if (disposed) + throw new ObjectDisposedException("GameWindow"); + + if (!IsExiting && Exists) + { + CancelEventArgs e = new CancelEventArgs(); + Closing(this, e); + if (e.Cancel) + return; + + if (HasMainLoop) + { + if (main_loop_thread_id == Thread.CurrentThread.ManagedThreadId) + ExitInternal(); + else + ExitAsync(); + } + } + } } #endregion @@ -427,25 +429,6 @@ namespace OpenTK #endregion -#if false - - #region public IInputDriver InputDriver - - /// - /// Gets an interface to the InputDriver used to obtain Keyboard, Mouse and Joystick input. - /// - public IInputDriver InputDriver - { - get - { - return null; - } - } - - #endregion - -#endif - #region public void Run() /// @@ -484,13 +467,21 @@ namespace OpenTK /// The frequency of RenderFrame events. public void Run(double updates_per_second, double frames_per_second) { - if (disposed) throw new ObjectDisposedException("GameWindow"); + if (disposed) + throw new ObjectDisposedException("GameWindow"); + try { + // Necessary to be here, otherwise Exit() wouldn't work correctly when called inside OnLoad(). + hasMainLoop = true; + main_loop_thread_id = Thread.CurrentThread.ManagedThreadId; + if (updates_per_second < 0.0 || updates_per_second > 200.0) - throw new ArgumentOutOfRangeException("updates_per_second", updates_per_second, "Parameter should be inside the range [0.0, 200.0]"); + throw new ArgumentOutOfRangeException("updates_per_second", updates_per_second, + "Parameter should be inside the range [0.0, 200.0]"); if (frames_per_second < 0.0 || frames_per_second > 200.0) - throw new ArgumentOutOfRangeException("frames_per_second", frames_per_second, "Parameter should be inside the range [0.0, 200.0]"); + throw new ArgumentOutOfRangeException("frames_per_second", frames_per_second, + "Parameter should be inside the range [0.0, 200.0]"); TargetUpdateFrequency = updates_per_second; TargetRenderFrequency = frames_per_second; @@ -520,21 +511,12 @@ namespace OpenTK //sleep_granularity = System.Math.Round(1000.0 * update_watch.Elapsed.TotalSeconds / test_times, MidpointRounding.AwayFromZero) / 1000.0; //update_watch.Reset(); // We don't want to affect the first UpdateFrame! - try - { - OnLoadInternal(EventArgs.Empty); - } - catch (Exception e) - { - Debug.WriteLine(String.Format("OnLoad failed: {0}", e.ToString())); - return; - } + OnLoadInternal(EventArgs.Empty); //Debug.Print("Elevating priority."); //Thread.CurrentThread.Priority = ThreadPriority.AboveNormal; Debug.Print("Entering main loop."); - hasMainLoop = true; while (!isExiting) { ProcessEvents(); @@ -966,6 +948,15 @@ namespace OpenTK #endregion + #region --- Events --- + + /// + /// Occurs when the GameWindow is about to close. + /// + public event EventHandler Closing = delegate(object sender, CancelEventArgs e) { }; + + #endregion + #region --- GameWindow Timing --- // TODO: Disabled because it is not reliable enough. Use vsync as a workaround. @@ -1000,12 +991,11 @@ namespace OpenTK if (disposed) throw new ObjectDisposedException("GameWindow"); if (value <= 0.005) { - target_render_period = target_render_period_doubled = 0.0; + target_render_period = 0.0; } else if (value <= 1.0) { target_render_period = value; - target_render_period_doubled = 2.0 * target_render_period; } else Debug.Print("Target render period clamped to 1.0 seconds."); }