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.");
}