2007-07-23 02:15:18 +02:00
#region - - - License - - -
2008-01-31 14:15:17 +01:00
/ * Licensed under the MIT / X11 license .
* Copyright ( c ) 2006 - 2008 the OpenTK Team .
* This notice may not be removed from any source distribution .
2008-03-24 14:12:02 +01:00
* See license . txt for licensing details .
2007-07-23 02:15:18 +02:00
* /
#endregion
using System ;
using System.Collections.Generic ;
using System.Text ;
2007-07-27 00:56:55 +02:00
using System.Diagnostics ;
2008-01-31 14:15:17 +01:00
using System.Threading ;
2007-07-23 02:15:18 +02:00
using OpenTK.Platform ;
2007-08-06 11:22:04 +02:00
using OpenTK.Input ;
2008-02-02 01:58:26 +01:00
using OpenTK.Graphics.OpenGL ;
using OpenTK.Graphics.OpenGL.Enums ;
2008-01-31 14:15:17 +01:00
using OpenTK.Graphics ;
2007-07-23 02:15:18 +02:00
namespace OpenTK
{
2007-09-26 13:50:44 +02:00
/// <summary>
2007-11-04 16:41:10 +01:00
/// The GameWindow class contains cross-platform methods to create and render on an OpenGL
2007-11-02 00:28:31 +01:00
/// window, handle input and load resources.
2007-09-26 13:50:44 +02:00
/// </summary>
/// <remarks>
/// GameWindow contains several events you can hook or override to add your custom logic:
/// <list>
2007-11-04 16:41:10 +01:00
/// <item>
/// OnLoad: Occurs after creating the OpenGL context, but before entering the main loop.
/// Override to load resources.
2007-11-02 00:28:31 +01:00
/// </item>
2007-11-04 16:41:10 +01:00
/// <item>
/// OnUnload: Occurs after exiting the main loop, but before deleting the OpenGL context.
/// Override to unload resources.
2007-11-02 00:28:31 +01:00
/// </item>
2007-11-04 16:41:10 +01:00
/// <item>
/// OnResize: Occurs whenever GameWindow is resized. You should update the OpenGL Viewport
/// and Projection Matrix here.
2007-11-02 00:28:31 +01:00
/// </item>
2007-11-04 16:41:10 +01:00
/// <item>
/// OnUpdateFrame: Occurs at the specified logic update rate. Override to add your game
/// logic.
2007-11-02 00:28:31 +01:00
/// </item>
2007-11-04 16:41:10 +01:00
/// <item>
/// OnRenderFrame: Occurs at the specified frame render rate. Override to add your
/// rendering code.
2007-11-02 00:28:31 +01:00
/// </item>
2007-09-26 13:50:44 +02:00
/// </list>
2007-11-04 16:41:10 +01:00
/// Call the Run() method to start the application's main loop. Run(double, double) takes two
2007-11-02 00:28:31 +01:00
/// parameters that
2007-09-26 13:50:44 +02:00
/// specify the logic update rate, and the render update rate.
/// </remarks>
2007-11-10 19:26:05 +01:00
public class GameWindow : IDisposable /* : IGameWindow*/
2007-07-23 02:15:18 +02:00
{
2007-08-05 15:42:31 +02:00
#region - - - Fields - - -
2007-09-26 13:50:44 +02:00
INativeGLWindow glWindow ;
2008-02-02 14:39:23 +01:00
//DisplayMode mode;
2007-07-23 02:15:18 +02:00
2007-09-26 13:50:44 +02:00
ResizeEventArgs resizeEventArgs = new ResizeEventArgs ( ) ;
2007-08-05 15:42:31 +02:00
2008-12-09 11:07:05 +01:00
bool isExiting = false ;
2008-02-28 14:57:07 +01:00
bool hasMainLoop ;
2007-09-26 13:50:44 +02:00
bool disposed ;
2007-08-06 11:22:04 +02:00
2007-09-30 14:44:42 +02:00
double update_period , render_period ;
double target_update_period , target_render_period , target_render_period_doubled ;
// TODO: Implement these:
2008-01-20 20:29:42 +01:00
double update_time , render_time ; //, event_time;
2007-10-15 13:12:56 +02:00
//bool allow_sleep = true; // If true, GameWindow will call Timer.Sleep() if there is enough time.
2007-09-26 13:50:44 +02:00
int width , height ;
2007-09-30 14:44:42 +02:00
VSyncMode vsync ;
2007-11-10 19:26:05 +01:00
//InputDriver input_driver;
2007-10-19 22:03:53 +02:00
2008-01-31 14:17:42 +01:00
IGraphicsContext glContext ;
2007-12-09 19:15:51 +01:00
2007-09-26 13:50:44 +02:00
#endregion
2007-07-23 02:15:18 +02:00
#region - - - Contructors - - -
2008-03-01 14:15:31 +01:00
#region public GameWindow ( )
2007-10-19 22:03:53 +02:00
2008-03-01 14:15:31 +01:00
/// <summary>Constructs a new GameWindow with sensible default attributes..</summary>
public GameWindow ( )
: this ( 640 , 480 , GraphicsMode . Default , "OpenTK Game Window" , 0 , DisplayDevice . Default ) { }
2007-10-19 22:03:53 +02:00
2008-03-01 14:15:31 +01:00
#endregion
#region public GameWindow ( int width , int height )
/// <summary>Constructs a new GameWindow with the specified attributes.</summary>
/// <param name="width">The width of the GameWindow in pixels.</param>
/// <param name="height">The height of the GameWindow in pixels.</param>
public GameWindow ( int width , int height )
: this ( width , height , GraphicsMode . Default , "OpenTK Game Window" , 0 , DisplayDevice . Default ) { }
#endregion
#region public GameWindow ( int width , int height , GraphicsMode mode )
2008-01-31 14:15:17 +01:00
2008-03-01 14:15:31 +01:00
/// <summary>Constructs a new GameWindow with the specified attributes.</summary>
/// <param name="width">The width of the GameWindow in pixels.</param>
/// <param name="height">The height of the GameWindow in pixels.</param>
/// <param name="mode">The OpenTK.Graphics.GraphicsMode of the GameWindow.</param>
public GameWindow ( int width , int height , GraphicsMode mode )
: this ( width , height , mode , "OpenTK Game Window" , 0 , DisplayDevice . Default ) { }
2008-01-31 14:15:17 +01:00
2008-03-01 14:15:31 +01:00
#endregion
#region public GameWindow ( int width , int height , GraphicsMode mode , string title )
/// <summary>Constructs a new GameWindow with the specified attributes.</summary>
/// <param name="width">The width of the GameWindow in pixels.</param>
/// <param name="height">The height of the GameWindow in pixels.</param>
/// <param name="mode">The OpenTK.Graphics.GraphicsMode of the GameWindow.</param>
/// <param name="title">The title of the GameWindow.</param>
public GameWindow ( int width , int height , GraphicsMode mode , string title )
: this ( width , height , mode , title , 0 , DisplayDevice . Default ) { }
#endregion
2008-01-31 14:15:17 +01:00
2008-03-01 14:15:31 +01:00
#region public GameWindow ( int width , int height , GraphicsMode mode , string title , GameWindowFlags options )
2008-01-31 14:15:17 +01:00
2008-03-01 14:15:31 +01:00
/// <summary>Constructs a new GameWindow with the specified attributes.</summary>
/// <param name="width">The width of the GameWindow in pixels.</param>
/// <param name="height">The height of the GameWindow in pixels.</param>
/// <param name="mode">The OpenTK.Graphics.GraphicsMode of the GameWindow.</param>
/// <param name="title">The title of the GameWindow.</param>
/// <param name="options">GameWindow options regarding window appearance and behavior.</param>
public GameWindow ( int width , int height , GraphicsMode mode , string title , GameWindowFlags options )
: this ( width , height , mode , title , options , DisplayDevice . Default ) { }
#endregion
#region public GameWindow ( int width , int height , GraphicsMode mode , string title , GameWindowFlags options , DisplayDevice device )
/// <summary>Constructs a new GameWindow with the specified attributes.</summary>
/// <param name="width">The width of the GameWindow in pixels.</param>
/// <param name="height">The height of the GameWindow in pixels.</param>
/// <param name="mode">The OpenTK.Graphics.GraphicsMode of the GameWindow.</param>
/// <param name="title">The title of the GameWindow.</param>
/// <param name="options">GameWindow options regarding window appearance and behavior.</param>
/// <param name="device">The OpenTK.Graphics.DisplayDevice to construct the GameWindow in.</param>
public GameWindow ( int width , int height , GraphicsMode mode , string title , GameWindowFlags options , DisplayDevice device )
2007-07-23 02:15:18 +02:00
{
2008-03-01 14:15:31 +01:00
if ( width < = 0 ) throw new ArgumentOutOfRangeException ( "width" , "Must be greater than zero." ) ;
if ( height < = 0 ) throw new ArgumentOutOfRangeException ( "width" , "Must be greater than zero." ) ;
if ( mode = = null )
mode = GraphicsMode . Default ;
if ( device = = null )
device = DisplayDevice . Default ;
2009-02-20 19:57:57 +01:00
glWindow = Platform . Factory . CreateNativeGLWindow ( ) ;
2008-01-15 01:10:56 +01:00
glWindow . Destroy + = glWindow_Destroy ;
2008-02-28 14:57:07 +01:00
try
{
2008-03-01 14:15:31 +01:00
glWindow . CreateWindow ( width , height , mode , out glContext ) ;
2008-03-03 13:44:56 +01:00
glContext . MakeCurrent ( this . WindowInfo ) ;
2008-03-01 14:15:31 +01:00
( glContext as IGraphicsContextInternal ) . LoadAll ( ) ;
2008-02-28 14:57:07 +01:00
}
2008-03-01 14:15:31 +01:00
//catch (GraphicsContextException e)
catch ( Exception e )
2008-02-28 14:57:07 +01:00
{
2008-03-01 14:15:31 +01:00
Debug . Print ( e . ToString ( ) ) ;
2008-02-28 14:57:07 +01:00
glWindow . DestroyWindow ( ) ;
throw ;
}
2008-03-01 14:15:31 +01:00
2008-01-31 14:15:17 +01:00
this . Title = title ;
2008-03-01 14:15:31 +01:00
if ( ( options & GameWindowFlags . Fullscreen ) ! = 0 )
2008-01-31 14:15:17 +01:00
{
2008-03-03 13:44:56 +01:00
device . ChangeResolution ( width , height , mode . ColorFormat . BitsPerPixel , 0 ) ;
2009-02-20 19:57:57 +01:00
this . WindowState = WindowState . Fullscreen ;
//throw new NotImplementedException();
2008-01-31 14:15:17 +01:00
}
2008-03-01 14:15:31 +01:00
2008-01-31 14:15:17 +01:00
this . VSync = VSyncMode . On ; //VSyncMode.Adaptive;
2009-02-20 19:57:57 +01:00
glWindow . Resize + = new ResizeEvent ( glWindow_Resize ) ;
}
void glWindow_Resize ( object sender , ResizeEventArgs e )
{
Debug . Print ( "glWindow_Resize event fired." ) ;
2008-03-01 14:15:31 +01:00
2009-02-20 19:57:57 +01:00
OnResizeInternal ( e ) ;
2008-01-31 14:15:17 +01:00
}
2009-02-20 19:57:57 +01:00
2008-03-01 14:15:31 +01:00
#endregion
2008-01-31 14:15:17 +01:00
/// <summary>
/// Constructs a new GameWindow, and opens a render window with the specified DisplayMode.
/// </summary>
/// <param name="mode">The DisplayMode of the GameWindow.</param>
[Obsolete]
public GameWindow ( DisplayMode mode )
2008-03-01 14:15:31 +01:00
: this ( mode . Width , mode . Height , mode . ToGraphicsMode ( ) , "OpenTK Game Window" , mode . Fullscreen ? GameWindowFlags . Fullscreen : 0 ) { }
2008-01-31 14:15:17 +01:00
/// <summary>
/// Constructs a new GameWindow with the specified title, and opens a render window with the
/// specified DisplayMode.
/// </summary>
/// <param name="mode">The DisplayMode of the GameWindow.</param>
/// <param name="title">The Title of the GameWindow.</param>
[Obsolete]
public GameWindow ( DisplayMode mode , string title )
2008-03-01 14:15:31 +01:00
: this ( mode . Width , mode . Height , mode . ToGraphicsMode ( ) , title , mode . Fullscreen ? GameWindowFlags . Fullscreen : 0 ) { }
#endregion
#region - - - Private Methods - - -
#region void glWindow_Destroy ( object sender , EventArgs e )
2007-09-26 13:50:44 +02:00
2007-08-06 13:22:18 +02:00
void glWindow_Destroy ( object sender , EventArgs e )
{
2007-08-06 14:13:50 +02:00
glWindow . Destroy - = glWindow_Destroy ;
2007-11-11 21:10:08 +01:00
this . Exit ( ) ;
2007-07-23 02:15:18 +02:00
}
#endregion
2008-03-01 14:15:31 +01:00
#region void ExitInternal ( )
/// <internal />
/// <summary>Stops the main loop.</summary>
void ExitInternal ( )
{
2009-02-20 19:57:57 +01:00
//Debug.Print("Firing GameWindowExitException");
if ( HasMainLoop )
{
throw new GameWindowExitException ( ) ;
}
if ( CloseWindow ! = null )
{
CloseWindow ( this , EventArgs . Empty ) ;
}
2008-03-01 14:15:31 +01:00
}
2009-02-20 19:57:57 +01:00
public event EventHandler CloseWindow ;
2008-03-01 14:15:31 +01:00
void CallExitInternal ( GameWindow sender , UpdateFrameEventArgs e )
{
UpdateFrame - = CallExitInternal ;
sender . ExitInternal ( ) ;
}
#endregion
2008-03-03 13:44:56 +01:00
#region bool MustResize
bool MustResize
{
get { return glWindow . Width ! = this . Width | | glWindow . Height ! = this . Height ; }
}
#endregion
#region bool HasMainLoop
bool HasMainLoop
{
get { return hasMainLoop ; }
set { hasMainLoop = value ; }
}
#endregion
2008-03-01 14:15:31 +01:00
#endregion
2008-04-13 23:32:04 +02:00
#region - - - Public Members - - -
2007-08-04 14:09:58 +02:00
2008-01-06 03:29:54 +01:00
#region public virtual void Exit ( )
2007-08-04 14:09:58 +02:00
/// <summary>
2008-02-28 14:57:07 +01:00
/// Gracefully exits the GameWindow. May only be called from the thread where the GameWindow was created.
2007-08-04 14:09:58 +02:00
/// </summary>
2008-01-06 03:29:54 +01:00
/// <remarks>
/// <para>Override if you want to provide yor own exit sequence.</para>
/// <para>If you override this method, place a call to base.Exit(), to ensure
/// proper OpenTK shutdown.</para>
/// </remarks>
2007-08-06 11:22:04 +02:00
public virtual void Exit ( )
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2008-02-28 14:57:07 +01:00
//glWindow.DestroyWindow();
//while (glWindow.Exists)
// glWindow.ProcessEvents();
if ( HasMainLoop )
ExitAsync ( ) ;
else
ExitInternal ( ) ;
//isExiting = true;
2008-01-15 13:08:09 +01:00
//UpdateFrame += CallExitInternal;
2008-01-06 03:29:54 +01:00
}
#endregion
2008-02-28 14:57:07 +01:00
#region public virtual void ExitAsync ( )
2008-01-06 03:29:54 +01:00
/// <summary>
2008-02-28 14:57:07 +01:00
/// Gracefully exits the GameWindow. May be called from any thread.
2008-01-06 03:29:54 +01:00
/// </summary>
2008-02-28 14:57:07 +01:00
/// <remarks>
/// <para>Override if you want to provide yor own exit sequence.</para>
/// <para>If you override this method, place a call to base.ExitAsync(), to ensure
/// proper OpenTK shutdown.</para>
/// </remarks>
public virtual void ExitAsync ( )
{
2008-04-13 20:44:23 +02:00
//isExiting = true;
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2008-02-28 14:57:07 +01:00
UpdateFrame + = CallExitInternal ;
}
#endregion
2007-07-23 02:15:18 +02:00
#region public bool IsIdle
2007-08-04 14:09:58 +02:00
/// <summary>
/// Gets a value indicating whether the current GameWindow is idle.
/// If true, the OnUpdateFrame and OnRenderFrame functions should be called.
/// </summary>
2007-07-23 02:15:18 +02:00
public bool IsIdle
{
2008-03-26 20:43:57 +01:00
get { if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ; return glWindow . IsIdle ; }
2007-07-23 02:15:18 +02:00
}
#endregion
#region public bool Fullscreen
2007-09-26 17:43:28 +02:00
/// <summary>
/// TODO: This property is not implemented.
/// Gets or sets a value indicating whether the GameWindow is in fullscrren mode.
/// </summary>
2008-04-20 19:54:16 +02:00
//public bool Fullscreen
//{
// get { if (disposed) throw new ObjectDisposedException("GameWindow"); return glWindow.Fullscreen; }
// set { if (disposed) throw new ObjectDisposedException("GameWindow"); glWindow.Fullscreen = value; }
//}
2007-07-23 02:15:18 +02:00
#endregion
2008-01-31 14:17:42 +01:00
#region public IGraphicsContext Context
2007-07-23 02:15:18 +02:00
2007-08-04 14:09:58 +02:00
/// <summary>
2008-03-26 20:43:57 +01:00
/// Returns the opengl IGraphicsContext associated with the current GameWindow.
2007-08-04 14:09:58 +02:00
/// </summary>
2008-01-31 14:17:42 +01:00
public IGraphicsContext Context
2007-07-23 02:15:18 +02:00
{
2008-04-13 20:44:23 +02:00
get
{
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
return glContext ;
2008-03-26 20:43:57 +01:00
}
2007-07-23 02:15:18 +02:00
}
#endregion
2007-08-06 11:22:04 +02:00
#region public bool Exists
2007-07-23 02:15:18 +02:00
2007-08-04 14:09:58 +02:00
/// <summary>
2007-09-26 13:50:44 +02:00
/// Gets a value indicating whether a render window exists.
2007-08-04 14:09:58 +02:00
/// </summary>
2007-08-06 11:22:04 +02:00
public bool Exists
2007-07-23 02:15:18 +02:00
{
2007-08-10 18:55:24 +02:00
get { return glWindow = = null ? false : glWindow . Exists ; }
2007-07-23 02:15:18 +02:00
}
#endregion
2007-09-26 13:50:44 +02:00
#region public string Text
/// <summary>
/// Gets or sets the GameWindow title.
/// </summary>
public string Title
{
get
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-09-26 13:50:44 +02:00
return glWindow . Title ;
}
set
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-09-26 13:50:44 +02:00
glWindow . Title = value ;
}
}
#endregion
#region public bool Visible
2008-05-04 18:34:38 +02:00
#if false
2007-09-26 13:50:44 +02:00
/// <summary>
2007-09-26 17:43:28 +02:00
/// TODO: This property is not implemented
2007-09-26 13:50:44 +02:00
/// Gets or sets a value indicating whether the GameWindow is visible.
/// </summary>
public bool Visible
{
get
{
2007-09-26 17:43:28 +02:00
throw new NotImplementedException ( ) ;
2007-11-06 14:37:19 +01:00
//return glWindow.Visible;
2007-09-26 13:50:44 +02:00
}
set
{
2007-09-26 17:43:28 +02:00
throw new NotImplementedException ( ) ;
2007-11-06 14:37:19 +01:00
//glWindow.Visible = value;
2007-09-26 13:50:44 +02:00
}
}
2008-05-04 18:34:38 +02:00
#endif
2007-09-26 13:50:44 +02:00
#endregion
2007-08-05 15:42:31 +02:00
#region public IWindowInfo WindowInfo
public IWindowInfo WindowInfo
{
2008-03-26 20:43:57 +01:00
get { if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ; return glWindow . WindowInfo ; }
2007-08-05 15:42:31 +02:00
}
#endregion
2008-03-01 14:15:31 +01:00
2007-10-19 22:03:53 +02:00
#if false
2007-08-05 15:42:31 +02:00
2007-09-26 13:50:44 +02:00
#region public IInputDriver InputDriver
/// <summary>
/// Gets an interface to the InputDriver used to obtain Keyboard, Mouse and Joystick input.
/// </summary>
public IInputDriver InputDriver
{
get
{
2007-10-19 22:03:53 +02:00
return null ;
2007-09-26 13:50:44 +02:00
}
}
#endregion
2007-10-19 22:03:53 +02:00
#endif
2008-06-22 20:07:22 +02:00
#region public void Run ( )
2007-08-04 14:09:58 +02:00
2007-07-23 02:15:18 +02:00
/// <summary>
2007-10-15 13:12:56 +02:00
/// Enters the game loop of the GameWindow updating and rendering at the maximum possible frequency.
2007-07-23 02:15:18 +02:00
/// </summary>
2007-10-15 13:12:56 +02:00
/// <see cref="public virtual void Run(double update_frequency, double render_frequency)"/>
2007-09-23 14:37:07 +02:00
public void Run ( )
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-09-23 14:37:07 +02:00
Run ( 0.0 , 0.0 ) ;
}
2008-06-22 20:07:22 +02:00
#endregion
#region public void Run ( double updateFrequency )
2007-09-23 14:37:07 +02:00
/// <summary>
2007-10-15 13:12:56 +02:00
/// Enters the game loop of the GameWindow updating the specified update frequency, while maintaining the
2007-09-23 14:37:07 +02:00
/// maximum possible render frequency.
/// </summary>
/// <see cref="public virtual void Run(double updateFrequency, double renderFrequency)"/>
public void Run ( double updateFrequency )
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-09-23 14:37:07 +02:00
Run ( updateFrequency , 0.0 ) ;
}
2008-06-22 20:07:22 +02:00
#endregion
#region public void Run ( double updates_per_second , double frames_per_second )
2007-09-23 14:37:07 +02:00
/// <summary>
2007-10-15 13:12:56 +02:00
/// Enters the game loop of the GameWindow updating and rendering at the specified frequency.
2007-09-23 14:37:07 +02:00
/// </summary>
2007-10-15 13:12:56 +02:00
/// <param name="updates_per_second">The frequency of UpdateFrame events.</param>
/// <param name="frames_per_second">The frequency of RenderFrame events.</param>
2007-09-30 14:44:42 +02:00
public void Run ( double updates_per_second , double frames_per_second )
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-10-20 16:22:39 +02:00
try
2007-09-30 14:44:42 +02:00
{
2007-10-20 16:22:39 +02:00
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]" ) ;
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]" ) ;
2007-09-30 14:44:42 +02:00
2007-10-20 16:22:39 +02:00
TargetUpdateFrequency = updates_per_second ;
TargetRenderFrequency = frames_per_second ;
2007-09-30 14:44:42 +02:00
2007-10-20 16:22:39 +02:00
Stopwatch update_watch = new Stopwatch ( ) , render_watch = new Stopwatch ( ) ;
double time , next_render = 0.0 , next_update = 0.0 , update_time_counter = 0.0 ;
int num_updates = 0 ;
UpdateFrameEventArgs update_args = new UpdateFrameEventArgs ( ) ;
RenderFrameEventArgs render_args = new RenderFrameEventArgs ( ) ;
2007-09-30 14:44:42 +02:00
2007-11-12 00:32:58 +01:00
update_watch . Reset ( ) ;
render_watch . Reset ( ) ;
2007-10-26 17:54:35 +02:00
//double sleep_granularity; // In seconds.
2007-09-30 14:44:42 +02:00
2007-10-20 16:22:39 +02:00
//GC.Collect(2);
//GC.WaitForPendingFinalizers();
//GC.Collect(2);
2007-09-30 14:44:42 +02:00
2007-10-20 16:22:39 +02:00
// Find the minimum granularity of the Thread.Sleep() function.
// TODO: Disabled - see comment on Thread.Sleep() problems below.
//update_watch.Start();
//const int test_times = 5;
//for (int i = test_times; --i > 0; )
// Thread.Sleep(1);
//update_watch.Stop();
//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!
2007-10-15 13:12:56 +02:00
2008-03-01 14:15:31 +01:00
try
{
OnLoadInternal ( EventArgs . Empty ) ;
}
catch ( Exception e )
{
2008-11-23 10:34:33 +01:00
Debug . WriteLine ( String . Format ( "OnLoad failed: {0}" , e . ToString ( ) ) ) ;
2008-03-01 14:15:31 +01:00
return ;
}
2007-09-30 14:44:42 +02:00
2008-01-15 13:08:09 +01:00
//Debug.Print("Elevating priority.");
//Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;
2007-11-12 08:45:34 +01:00
2007-11-08 16:57:10 +01:00
Debug . Print ( "Entering main loop." ) ;
2008-02-28 14:57:07 +01:00
hasMainLoop = true ;
2007-10-20 16:22:39 +02:00
while ( ! isExiting )
2007-09-30 14:44:42 +02:00
{
2007-10-20 16:22:39 +02:00
ProcessEvents ( ) ;
2007-09-30 14:44:42 +02:00
2007-10-20 16:22:39 +02:00
// Raise UpdateFrame events
time = update_watch . Elapsed . TotalSeconds ;
if ( time > 1.0 )
time = 1.0 ;
while ( next_update - time < = 0.0 )
{
next_update = next_update - time + TargetUpdatePeriod ;
2007-10-26 17:54:35 +02:00
if ( next_update < - 1.0 ) // Cap the maximum time drift, to avoid lengthy catch-up games.
next_update = - 1.0 ;
2007-10-20 16:22:39 +02:00
update_time_counter + = time ;
+ + num_updates ;
update_watch . Reset ( ) ;
update_watch . Start ( ) ;
update_args . Time = time ;
OnUpdateFrameInternal ( update_args ) ;
2007-10-26 17:54:35 +02:00
update_time = update_watch . Elapsed . TotalSeconds ;
2007-10-20 16:22:39 +02:00
if ( TargetUpdateFrequency = = 0.0 )
break ;
time = update_watch . Elapsed . TotalSeconds ;
next_update - = time ;
update_time_counter + = time ;
2007-10-26 17:54:35 +02:00
// Allow up to 10 frames to be dropped. Prevents the application from hanging
// with very high update frequencies.
2007-10-20 16:22:39 +02:00
if ( num_updates > = 10 )
break ;
}
if ( num_updates > 0 )
{
update_period = update_time_counter / ( double ) num_updates ;
num_updates = 0 ;
update_time_counter = 0.0 ;
}
// Raise RenderFrame event
time = render_watch . Elapsed . TotalSeconds ;
if ( time > 1.0 )
time = 1.0 ;
double time_left = next_render - time ;
if ( VSync = = VSyncMode . Adaptive )
{
// Check if we have enough time for a vsync
2008-01-31 14:15:17 +01:00
if ( TargetRenderPeriod ! = 0 & & RenderTime > 2.0 * TargetRenderPeriod )
2007-10-20 16:22:39 +02:00
Context . VSync = false ;
else
Context . VSync = true ;
}
if ( time_left < = 0.0 )
{
next_render = time_left + TargetRenderPeriod ;
2007-10-26 17:54:35 +02:00
if ( next_render < - 1.0 ) // Cap the maximum time drift, to avoid lengthy catch-up games.
next_render = - 1.0 ;
2007-10-20 16:22:39 +02:00
render_watch . Reset ( ) ;
render_watch . Start ( ) ;
render_period = render_args . Time = time ;
render_args . ScaleFactor = RenderPeriod / UpdatePeriod ;
OnRenderFrameInternal ( render_args ) ;
2007-10-26 17:54:35 +02:00
render_time = render_watch . Elapsed . TotalSeconds ;
2007-10-20 16:22:39 +02:00
}
// Yield CPU time, if the Thread.Sleep() granularity allows it.
// TODO: Disabled because it does not work reliably enough on all systems.
// Enable vsync as a workaround.
//if (AllowSleep && next_render > sleep_granularity && next_update > sleep_granularity)
//{
// Thread.Sleep((int)(1000.0 * System.Math.Min(next_render - sleep_granularity, next_update - sleep_granularity)));
//}
2007-08-06 13:22:18 +02:00
}
2007-07-23 02:15:18 +02:00
}
2007-11-11 20:28:13 +01:00
catch ( GameWindowExitException )
{
2008-03-03 13:44:56 +01:00
Debug . WriteLine ( "GameWindowExitException caught - exiting main loop." ) ;
2007-11-11 20:28:13 +01:00
}
2007-10-20 16:22:39 +02:00
finally
{
2007-11-11 21:10:08 +01:00
Debug . Print ( "Restoring priority." ) ;
2007-10-20 16:22:39 +02:00
Thread . CurrentThread . Priority = ThreadPriority . Normal ;
2007-10-15 13:12:56 +02:00
2007-10-20 16:22:39 +02:00
OnUnloadInternal ( EventArgs . Empty ) ;
2007-09-27 01:13:57 +02:00
2008-03-03 13:44:56 +01:00
if ( Exists )
2007-08-06 13:22:18 +02:00
{
2008-04-13 20:44:23 +02:00
glContext . Dispose ( ) ;
2008-03-26 20:43:57 +01:00
glContext = null ;
2008-04-13 20:44:23 +02:00
glWindow . DestroyWindow ( ) ;
2007-08-06 13:22:18 +02:00
}
2008-03-03 13:44:56 +01:00
while ( this . Exists )
this . ProcessEvents ( ) ;
2007-08-06 11:22:04 +02:00
}
2007-07-23 02:15:18 +02:00
}
2007-08-04 14:09:58 +02:00
#endregion
#region public void ProcessEvents ( )
/// <summary>
/// Processes operating system events until the GameWindow becomes idle.
/// </summary>
/// <remarks>
/// When overriding the default GameWindow game loop (provided by the Run() function)
/// you should call ProcessEvents() to ensure that your GameWindow responds to
/// operating system events.
/// <para>
/// Once ProcessEvents() returns, it is time to call update and render the next frame.
/// </para>
/// </remarks>
2007-07-23 02:15:18 +02:00
public void ProcessEvents ( )
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2008-01-15 14:01:55 +01:00
if ( ! isExiting )
glWindow . InputDriver . Poll ( ) ;
2007-07-23 02:15:18 +02:00
glWindow . ProcessEvents ( ) ;
2007-10-20 12:28:02 +02:00
if ( MustResize )
{
resizeEventArgs . Width = glWindow . Width ;
resizeEventArgs . Height = glWindow . Height ;
OnResizeInternal ( resizeEventArgs ) ;
}
2007-07-23 02:15:18 +02:00
}
2007-08-04 14:09:58 +02:00
#endregion
2007-09-26 13:50:44 +02:00
#region OnRenderFrame ( RenderFrameEventArgs e )
2007-08-04 14:09:58 +02:00
/// <summary>
2007-09-26 13:50:44 +02:00
/// Raises the RenderFrame event, and calls the public function.
2007-08-04 14:09:58 +02:00
/// </summary>
2007-09-26 13:50:44 +02:00
/// <param name="e"></param>
private void OnRenderFrameInternal ( RenderFrameEventArgs e )
2008-04-13 20:44:23 +02:00
{
2007-08-04 14:09:58 +02:00
if ( RenderFrame ! = null )
2007-08-06 13:22:18 +02:00
RenderFrame ( this , e ) ;
2007-09-26 13:50:44 +02:00
// Call the user's override.
OnRenderFrame ( e ) ;
}
/// <summary>
/// Override in derived classes to render a frame.
/// </summary>
/// <param name="e">Contains information necessary for frame rendering.</param>
/// <remarks>
/// The base implementation (base.OnRenderFrame) is empty, there is no need to call it.
/// </remarks>
public virtual void OnRenderFrame ( RenderFrameEventArgs e )
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-07-23 02:15:18 +02:00
}
2007-08-06 13:22:18 +02:00
/// <summary>
/// Occurs when it is time to render the next frame.
/// </summary>
public event RenderFrameEvent RenderFrame ;
2007-08-04 14:09:58 +02:00
#endregion
2007-09-26 13:50:44 +02:00
#region OnUpdateFrame ( UpdateFrameEventArgs e )
2007-08-04 14:09:58 +02:00
2007-09-26 13:50:44 +02:00
private void OnUpdateFrameInternal ( UpdateFrameEventArgs e )
2007-07-23 02:15:18 +02:00
{
2007-08-10 18:55:24 +02:00
if ( ! this . Exists & & ! this . IsExiting )
2007-08-04 14:09:58 +02:00
{
2007-10-20 12:28:02 +02:00
//Debug.Print("WARNING: UpdateFrame event raised without a valid render window. This may indicate a programming error. Creating render window.");
//mode = new DisplayMode(640, 480);
//this.CreateWindow(mode);
throw new InvalidOperationException ( "Cannot enter game loop without a render window" ) ;
2007-09-26 13:50:44 +02:00
}
2007-08-04 14:09:58 +02:00
if ( UpdateFrame ! = null )
2007-09-26 13:50:44 +02:00
{
2007-08-06 13:22:18 +02:00
UpdateFrame ( this , e ) ;
2007-09-26 13:50:44 +02:00
}
OnUpdateFrame ( e ) ;
}
/// <summary>
/// Override in derived classes to update a frame.
/// </summary>
/// <param name="e">Contains information necessary for frame updating.</param>
/// <remarks>
/// The base implementation (base.OnUpdateFrame) is empty, there is no need to call it.
/// </remarks>
public virtual void OnUpdateFrame ( UpdateFrameEventArgs e )
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-07-23 02:15:18 +02:00
}
2007-08-04 14:09:58 +02:00
/// <summary>
/// Occurs when it is time to update the next frame.
/// </summary>
public event UpdateFrameEvent UpdateFrame ;
2007-08-06 13:22:18 +02:00
#endregion
2007-09-26 13:50:44 +02:00
#region OnLoad ( EventArgs e )
2007-08-06 13:22:18 +02:00
2007-08-04 14:09:58 +02:00
/// <summary>
2007-09-26 13:50:44 +02:00
/// Occurs after establishing an OpenGL context, but before entering the main loop.
2007-08-04 14:09:58 +02:00
/// </summary>
2007-09-26 13:50:44 +02:00
public event LoadEvent Load ;
/// <summary>
/// Raises the Load event, and calls the user's OnLoad override.
/// </summary>
/// <param name="e"></param>
private void OnLoadInternal ( EventArgs e )
2007-08-06 13:22:18 +02:00
{
2008-03-01 14:15:31 +01:00
Debug . Print ( "Firing internal load event." ) ;
2007-10-20 12:28:02 +02:00
if ( MustResize )
{
resizeEventArgs . Width = glWindow . Width ;
resizeEventArgs . Height = glWindow . Height ;
OnResizeInternal ( resizeEventArgs ) ;
}
2007-09-27 01:13:57 +02:00
Debug . WriteLine ( String . Format ( "OpenGL driver information: {0}, {1}, {2}" ,
2007-11-04 16:41:10 +01:00
GL . GetString ( StringName . Renderer ) ,
GL . GetString ( StringName . Vendor ) ,
GL . GetString ( StringName . Version ) ) ) ;
2007-09-27 01:13:57 +02:00
2007-08-06 13:22:18 +02:00
if ( this . Load ! = null )
{
this . Load ( this , e ) ;
}
2007-09-26 13:50:44 +02:00
OnLoad ( e ) ;
2007-08-06 13:22:18 +02:00
}
/// <summary>
2007-09-26 13:50:44 +02:00
/// Occurs after establishing an OpenGL context, but before entering the main loop.
/// Override to load resources that should be maintained for the lifetime of the application.
2007-08-06 13:22:18 +02:00
/// </summary>
2007-09-26 13:50:44 +02:00
/// <param name="e">Not used.</param>
public virtual void OnLoad ( EventArgs e )
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-09-26 13:50:44 +02:00
}
2007-08-06 13:22:18 +02:00
#endregion
2007-07-23 02:15:18 +02:00
2007-09-27 01:13:57 +02:00
#region OnUnload ( EventArgs e )
/// <summary>
/// Occurs after after calling GameWindow.Exit, but before destroying the OpenGL context.
/// </summary>
public event UnloadEvent Unload ;
/// <summary>
/// Raises the Unload event, and calls the user's OnUnload override.
/// </summary>
/// <param name="e"></param>
private void OnUnloadInternal ( EventArgs e )
{
if ( this . Unload ! = null )
{
this . Unload ( this , e ) ;
}
OnUnload ( e ) ;
}
/// <summary>
/// Occurs after after calling GameWindow.Exit, but before destroying the OpenGL context.
/// Override to unload application resources.
/// </summary>
/// <param name="e">Not used.</param>
public virtual void OnUnload ( EventArgs e )
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-09-27 01:13:57 +02:00
}
#endregion
2007-08-06 11:22:04 +02:00
#region public bool IsExiting
/// <summary>
/// Gets a value indicating whether the shutdown sequence has been initiated
/// for this window, by calling GameWindow.Exit() or hitting the 'close' button.
/// If this property is true, it is no longer safe to use any OpenTK.Input or
2008-02-02 01:58:26 +01:00
/// OpenTK.Graphics.OpenGL functions or properties.
2007-08-06 11:22:04 +02:00
/// </summary>
public bool IsExiting
{
2008-03-26 20:43:57 +01:00
get { if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ; return isExiting ; }
2007-08-06 11:22:04 +02:00
}
#endregion
2007-09-26 13:50:44 +02:00
#region public Keyboard Keyboard
/// <summary>
/// Gets the primary Keyboard device, or null if no Keyboard exists.
/// </summary>
public KeyboardDevice Keyboard
{
get
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-11-10 19:26:05 +01:00
//if (input_driver.Keyboard.Count > 0)
// return input_driver.Keyboard[0];
//else
// return null;
if ( glWindow . InputDriver . Keyboard . Count > 0 )
return glWindow . InputDriver . Keyboard [ 0 ] ;
2007-09-26 13:50:44 +02:00
else
return null ;
}
}
#endregion
#region public Mouse Mouse
2007-08-06 11:22:04 +02:00
/// <summary>
2007-09-26 13:50:44 +02:00
/// Gets the primary Mouse device, or null if no Mouse exists.
2007-08-06 11:22:04 +02:00
/// </summary>
2007-09-26 13:50:44 +02:00
public MouseDevice Mouse
2007-08-06 11:22:04 +02:00
{
get
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-11-10 19:26:05 +01:00
//if (input_driver.Mouse.Count > 0)
// return input_driver.Mouse[0];
//else
// return null;
if ( glWindow . InputDriver . Mouse . Count > 0 )
return glWindow . InputDriver . Mouse [ 0 ] ;
2007-09-26 13:50:44 +02:00
else
return null ;
2007-08-06 11:22:04 +02:00
}
}
#endregion
2007-09-30 14:44:42 +02:00
#region public VSyncMode VSync
/// <summary>
/// Gets or sets the VSyncMode.
/// </summary>
public VSyncMode VSync
{
get
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-09-30 14:44:42 +02:00
return vsync ;
}
set
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-09-30 14:44:42 +02:00
if ( value = = VSyncMode . Off )
Context . VSync = false ;
2007-11-08 16:57:10 +01:00
else
2007-09-30 14:44:42 +02:00
Context . VSync = true ;
vsync = value ;
}
}
#endregion
#region public void SwapBuffers ( )
/// <summary>
/// Swaps the front and back buffer, presenting the rendered scene to the user.
/// Only useful in double- or triple-buffered formats.
/// </summary>
/// <remarks>Calling this function is equivalent to calling Context.SwapBuffers()</remarks>
public void SwapBuffers ( )
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2008-03-03 13:44:56 +01:00
this . Context . SwapBuffers ( ) ;
2007-09-30 14:44:42 +02:00
}
#endregion
2008-04-20 19:54:16 +02:00
#region public WindowState WindowState
/// <summary>
/// Gets or states the state of the GameWindow.
/// </summary>
public WindowState WindowState
2008-04-13 23:32:04 +02:00
{
get
{
return glWindow . WindowState ;
}
set
{
glWindow . WindowState = value ;
}
}
2007-09-30 14:44:42 +02:00
#endregion
2008-04-21 22:29:40 +02:00
#region public WindowBorder WindowBorder
/// <summary>
/// Gets or states the border of the GameWindow.
/// </summary>
public WindowBorder WindowBorder
{
get
{
return glWindow . WindowBorder ;
}
set
{
glWindow . WindowBorder = value ;
}
}
#endregion
2008-04-20 19:54:16 +02:00
#endregion
2007-09-30 14:44:42 +02:00
#region - - - GameWindow Timing - - -
2007-10-15 13:12:56 +02:00
// TODO: Disabled because it is not reliable enough. Use vsync as a workaround.
//#region public bool AllowSleep
//public bool AllowSleep
//{
// get { return allow_sleep; }
// set { allow_sleep = value; }
//}
//#endregion
2007-09-30 14:44:42 +02:00
#region public double TargetRenderPeriod
/// <summary>
2007-10-26 17:54:35 +02:00
/// Gets or sets a double representing the target render period, in seconds.
2007-09-30 14:44:42 +02:00
/// </summary>
/// <para>A value of 0.0 indicates that RenderFrame events are generated at the maximum possible frequency (i.e. only limited by the hardware's capabilities).</para>
/// <para>Values lower than 0.005 seconds (200Hz) are clamped to 0.0. Values higher than 1.0 seconds (1Hz) are clamped to 1.0.</para>
/// </remarks>
public double TargetRenderPeriod
{
get
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-09-30 14:44:42 +02:00
return target_render_period ;
}
set
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-09-30 14:44:42 +02:00
if ( value < = 0.005 )
{
target_render_period = target_render_period_doubled = 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." ) ;
}
}
#endregion
#region public double TargetRenderFrequency
/// <summary>
2007-10-26 17:54:35 +02:00
/// Gets or sets a double representing the target render frequency, in Herz.
2007-09-30 14:44:42 +02:00
/// </summary>
/// <remarks>
/// <para>A value of 0.0 indicates that RenderFrame events are generated at the maximum possible frequency (i.e. only limited by the hardware's capabilities).</para>
/// <para>Values lower than 1.0Hz are clamped to 1.0Hz. Values higher than 200.0Hz are clamped to 200.0Hz.</para>
/// </remarks>
public double TargetRenderFrequency
{
get
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-09-30 14:44:42 +02:00
if ( TargetRenderPeriod = = 0.0 )
return 0.0 ;
return 1.0 / TargetRenderPeriod ;
}
set
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-10-15 13:12:56 +02:00
if ( value < 1.0 )
2007-09-30 14:44:42 +02:00
{
TargetRenderPeriod = 0.0 ;
}
else if ( value < = 200.0 )
{
TargetRenderPeriod = 1.0 / value ;
}
else Debug . Print ( "Target render frequency clamped to 200.0Hz." ) ;
}
}
#endregion
#region public double TargetUpdatePeriod
/// <summary>
2007-10-26 17:54:35 +02:00
/// Gets or sets a double representing the target update period, in seconds.
2007-09-30 14:44:42 +02:00
/// </summary>
/// <remarks>
/// <para>A value of 0.0 indicates that UpdateFrame events are generated at the maximum possible frequency (i.e. only limited by the hardware's capabilities).</para>
/// <para>Values lower than 0.005 seconds (200Hz) are clamped to 0.0. Values higher than 1.0 seconds (1Hz) are clamped to 1.0.</para>
/// </remarks>
public double TargetUpdatePeriod
{
get
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-09-30 14:44:42 +02:00
return target_update_period ;
}
set
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-09-30 14:44:42 +02:00
if ( value < = 0.005 )
{
target_update_period = 0.0 ;
}
else if ( value < = 1.0 )
{
target_update_period = value ;
}
else Debug . Print ( "Target update period clamped to 1.0 seconds." ) ;
}
}
#endregion
#region public double TargetUpdateFrequency
/// <summary>
2007-10-26 17:54:35 +02:00
/// Gets or sets a double representing the target update frequency, in Herz.
2007-09-30 14:44:42 +02:00
/// </summary>
/// <remarks>
/// <para>A value of 0.0 indicates that UpdateFrame events are generated at the maximum possible frequency (i.e. only limited by the hardware's capabilities).</para>
/// <para>Values lower than 1.0Hz are clamped to 1.0Hz. Values higher than 200.0Hz are clamped to 200.0Hz.</para>
/// </remarks>
public double TargetUpdateFrequency
{
get
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-09-30 14:44:42 +02:00
if ( TargetUpdatePeriod = = 0.0 )
return 0.0 ;
return 1.0 / TargetUpdatePeriod ;
}
set
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-10-15 13:12:56 +02:00
if ( value < 1.0 )
2007-09-30 14:44:42 +02:00
{
TargetUpdatePeriod = 0.0 ;
}
else if ( value < = 200.0 )
{
TargetUpdatePeriod = 1.0 / value ;
}
else Debug . Print ( "Target update frequency clamped to 200.0Hz." ) ;
}
}
#endregion
#region public double RenderFrequency
/// <summary>
2007-10-26 17:54:35 +02:00
/// Gets a double representing the actual frequency of RenderFrame events, in Herz (i.e. FPS or Frames Per Second).
2007-09-30 14:44:42 +02:00
/// </summary>
public double RenderFrequency
{
get
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-09-30 14:44:42 +02:00
if ( render_period = = 0.0 )
return 1.0 ;
return 1.0 / render_period ;
}
}
#endregion
#region public double RenderPeriod
/// <summary>
2007-10-26 17:54:35 +02:00
/// Gets a double representing the period of RenderFrame events, in seconds.
2007-09-30 14:44:42 +02:00
/// </summary>
public double RenderPeriod
{
get
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-09-30 14:44:42 +02:00
return render_period ;
}
}
#endregion
#region public double UpdateFrequency
/// <summary>
2007-10-26 17:54:35 +02:00
/// Gets a double representing the frequency of UpdateFrame events, in Herz.
2007-09-30 14:44:42 +02:00
/// </summary>
public double UpdateFrequency
{
get
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-09-30 14:44:42 +02:00
if ( update_period = = 0.0 )
return 1.0 ;
return 1.0 / update_period ;
}
}
#endregion
#region public double UpdatePeriod
/// <summary>
2007-10-26 17:54:35 +02:00
/// Gets a double representing the period of UpdateFrame events, in seconds.
2007-09-30 14:44:42 +02:00
/// </summary>
public double UpdatePeriod
{
get
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-09-30 14:44:42 +02:00
return update_period ;
}
}
#endregion
2007-10-26 17:54:35 +02:00
#region public double RenderTime
/// <summary>
/// Gets a double representing the time spent in the RenderFrame function, in seconds.
/// </summary>
public double RenderTime
{
2008-03-26 20:43:57 +01:00
get { if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ; return render_time ; }
protected set { if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ; render_time = value ; }
2007-10-26 17:54:35 +02:00
}
#endregion
#region public double RenderTime
/// <summary>
/// Gets a double representing the time spent in the UpdateFrame function, in seconds.
/// </summary>
public double UpdateTime
{
2008-03-26 20:43:57 +01:00
get { if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ; return update_time ; }
2007-10-26 17:54:35 +02:00
}
#endregion
2007-07-23 02:15:18 +02:00
#endregion
#region - - - IResizable Members - - -
#region public int Width , Height
2007-08-21 14:04:01 +02:00
/// <summary>
/// Gets or sets the Width of the GameWindow's rendering area, in pixels.
/// </summary>
2007-07-23 02:15:18 +02:00
public int Width
{
2008-03-26 20:43:57 +01:00
get { if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ; return width ; }
2007-07-23 02:15:18 +02:00
set
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-07-23 02:15:18 +02:00
if ( value = = this . Width )
{
return ;
}
else if ( value > 0 )
{
glWindow . Width = value ;
}
else
{
throw new ArgumentOutOfRangeException (
"Width" ,
value ,
"Width must be greater than 0"
) ;
}
}
}
2007-08-21 14:04:01 +02:00
/// <summary>
/// Gets or sets the Height of the GameWindow's rendering area, in pixels.
/// </summary>
2007-07-23 02:15:18 +02:00
public int Height
{
2008-03-26 20:43:57 +01:00
get { if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ; return height ; }
2007-07-23 02:15:18 +02:00
set
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-07-23 02:15:18 +02:00
if ( value = = this . Height )
{
return ;
}
else if ( value > 0 )
{
glWindow . Height = value ;
}
else
{
throw new ArgumentOutOfRangeException (
"Height" ,
value ,
"Height must be greater than 0"
) ;
}
}
}
#endregion
#region public event ResizeEvent Resize ;
2007-09-26 13:50:44 +02:00
/// <summary>
/// Occurs when the GameWindow is resized. Derived classes should override the OnResize method for better performance.
/// </summary>
2009-02-20 19:57:57 +01:00
public event ResizeEvent Resize ;
2007-07-23 02:15:18 +02:00
/// <summary>
/// Raises the Resize event.
/// </summary>
2007-09-26 13:50:44 +02:00
/// <param name="e">Contains information about the Resize event.</param>
private void OnResizeInternal ( ResizeEventArgs e )
2007-07-23 02:15:18 +02:00
{
2008-03-01 14:15:31 +01:00
Debug . Print ( "Firing internal resize event: {0}." , e . ToString ( ) ) ;
2007-09-26 13:50:44 +02:00
this . width = e . Width ;
this . height = e . Height ;
2008-01-23 15:17:09 +01:00
2009-02-20 19:57:57 +01:00
if ( this . Resize ! = null )
this . Resize ( this , e ) ;
2007-09-26 13:50:44 +02:00
OnResize ( e ) ;
}
/// <summary>
/// Override in derived classes to respond to the Resize events.
/// </summary>
/// <param name="e">Contains information about the Resize event.</param>
protected virtual void OnResize ( ResizeEventArgs e )
2008-04-13 20:44:23 +02:00
{
2008-03-26 20:43:57 +01:00
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
2007-07-23 02:15:18 +02:00
}
#endregion
2008-01-26 15:02:58 +01:00
2007-08-21 14:04:01 +02:00
/ *
/// <summary>
/// Gets the Top coordinate of the GameWindow's rendering area, in pixel coordinates relative to the GameWindow's top left point.
/// </summary>
public int Top
{
get { return glWindow . Top ; }
}
/// <summary>
/// /// Gets the Bottom coordinate of the GameWindow's rendering area, in pixel coordinates relative to the GameWindow's top left point.
/// </summary>
public int Bottom
{
get { return glWindow . Bottom ; }
}
2007-07-23 02:15:18 +02:00
2007-08-21 14:04:01 +02:00
/// <summary>
/// Gets the Left coordinate of the GameWindow's rendering area, in pixel coordinates relative to the GameWindow's top left point.
/// </summary>
public int Left
{
get { return glWindow . Left ; }
}
/// <summary>
/// Gets the Right coordinate of the GameWindow's rendering area, in pixel coordinates relative to the GameWindow's top left point.
/// </summary>
public int Right
{
get { return glWindow . Right ; }
}
* /
2007-07-23 02:15:18 +02:00
#endregion
2008-01-23 15:17:09 +01:00
#if false // TODO: 0.9.2 (Linux support missing)
2008-01-06 03:29:54 +01:00
#region PointToClient
/// <summary>
/// Converts the screen coordinates of a specified point on the screen to client-area coordinates.
/// </summary>
/// <param name="p">A System.Drawing.Point structure that specifies the screen coordinates to be converted</param>
/// <returns>The client-area coordinates of the point. The new coordinates are relative to the upper-left corner of the GameWindow's client area.</returns>
public System . Drawing . Point PointToClient ( System . Drawing . Point p )
{
glWindow . PointToClient ( ref p ) ;
return p ;
}
#endregion
#region PointToScreen
/// <summary>
/// Converts the client-area coordinates of a specified point to screen coordinates.
/// </summary>
/// <param name="p">A System.Drawing.Point structure that specifies the client-area coordinates to be converted</param>
/// <returns>The screen coordinates of the point, relative to the upper-left corner of the screen. Note, a screen-coordinate point that is above the window's client area has a negative y-coordinate. Similarly, a screen coordinate to the left of a client area has a negative x-coordinate.</returns>
public System . Drawing . Point PointToScreen ( System . Drawing . Point p )
{
glWindow . PointToScreen ( ref p ) ;
return p ;
}
#endregion
2008-01-14 23:38:09 +01:00
#endif
2007-07-23 02:15:18 +02:00
#region - - - IDisposable Members - - -
2007-09-25 18:45:12 +02:00
/// <summary>
/// Disposes of the GameWindow, releasing all resources consumed by it.
/// </summary>
2008-07-10 17:20:48 +02:00
public void Dispose ( )
2008-04-13 20:44:23 +02:00
{
2008-07-10 17:20:48 +02:00
try
{
Dispose ( true ) ;
}
finally
{
DisposeInternal ( true ) ;
}
2008-03-26 20:43:57 +01:00
GC . SuppressFinalize ( this ) ;
2007-08-06 11:22:04 +02:00
}
2008-07-10 17:20:48 +02:00
private void DisposeInternal ( bool manual )
2007-08-06 11:22:04 +02:00
{
if ( ! disposed )
{
if ( manual )
{
2008-04-13 20:44:23 +02:00
if ( glContext ! = null )
2008-03-26 20:43:57 +01:00
{
2008-04-13 20:44:23 +02:00
glContext . Dispose ( ) ;
glContext = null ;
2008-03-26 20:43:57 +01:00
}
2008-03-03 13:44:56 +01:00
2007-08-06 11:22:04 +02:00
if ( glWindow ! = null )
{
glWindow . Dispose ( ) ;
glWindow = null ;
}
}
disposed = true ;
}
}
2008-07-10 17:20:48 +02:00
/// <summary>
/// Override to add custom cleanup logic.
/// </summary>
/// <param name="manual">True, if this method was called by the application; false if this was called by the finalizer thread.</param>
protected virtual void Dispose ( bool manual )
{
}
///// <summary>Finalizes unmanaged resources consumed by the GameWindow.</summary>
2008-03-26 20:43:57 +01:00
//~GameWindow()
//{
// Dispose(false);
2008-07-10 17:20:48 +02:00
// DisposeInternal(false);
2008-03-26 20:43:57 +01:00
//}
2007-09-25 18:45:12 +02:00
2007-07-23 02:15:18 +02:00
#endregion
}
2007-09-23 00:01:43 +02:00
2007-09-30 14:44:42 +02:00
#region public enum VSyncMode
/// <summary>
2008-01-06 03:29:54 +01:00
/// Enumerates available VSync modes.
2007-09-30 14:44:42 +02:00
/// </summary>
public enum VSyncMode
{
/// <summary>
/// Vsync disabled.
/// </summary>
Off = 0 ,
/// <summary>
/// VSync enabled.
/// </summary>
On ,
/// <summary>
/// VSync enabled, but automatically disabled if framerate falls below a specified limit.
/// </summary>
2007-10-15 13:12:56 +02:00
Adaptive ,
2007-09-30 14:44:42 +02:00
}
#endregion
#region - - - GameWindow Events - - -
public delegate void UpdateFrameEvent ( GameWindow sender , UpdateFrameEventArgs e ) ;
public delegate void RenderFrameEvent ( GameWindow sender , RenderFrameEventArgs e ) ;
public delegate void LoadEvent ( GameWindow sender , EventArgs e ) ;
public delegate void UnloadEvent ( GameWindow sender , EventArgs e ) ;
2007-09-23 00:01:43 +02:00
public class UpdateFrameEventArgs : EventArgs
{
2007-09-23 14:09:42 +02:00
private double time ;
/// <summary>
/// Gets the Time elapsed between frame updates, in seconds.
/// </summary>
public double Time
{
get { return time ; }
internal set { time = value ; }
}
}
public class RenderFrameEventArgs : EventArgs
{
private double time ;
2007-09-30 14:44:42 +02:00
private double scale_factor ;
2007-09-23 00:01:43 +02:00
/// <summary>
/// Gets the Time elapsed between frame updates, in seconds.
/// </summary>
2007-09-23 14:09:42 +02:00
public double Time
2007-09-23 00:01:43 +02:00
{
get { return time ; }
internal set { time = value ; }
}
2007-09-30 14:44:42 +02:00
public double ScaleFactor
{
get
{
return scale_factor ;
}
internal set
{
if ( value ! = 0.0 & & ! Double . IsNaN ( value ) )
scale_factor = value ;
else
scale_factor = 1.0 ;
}
}
2007-09-23 00:01:43 +02:00
}
2007-09-30 14:44:42 +02:00
#endregion
2007-10-15 13:12:56 +02:00
#region - - - GameWindow Exceptions - - -
2007-11-12 20:21:45 +01:00
[DebuggerNonUserCode]
2007-11-10 19:26:05 +01:00
class GameWindowExitException : ApplicationException
2007-10-15 13:12:56 +02:00
{
2007-11-11 21:23:25 +01:00
public override string Message
{
get
{
2007-11-12 20:21:45 +01:00
return
@ "This exception is a normal part of the GameWindow shutdown process and is completely harmless. While this warning will never be seen by end-users, Visual Studio reminds you that an exception is leaving your code unhandled, which can sometimes be a security breach.
You can disable this warning for this specific exception : select Debug - > Exceptions from the menu bar and click "" Add "" . Choose "" Common Language Runtime Exceptions "" , type "" OpenTK . GameWindowExitException "" in the box below and click "" Ok "" . Deselecting the "" User - unhandled "" checkbox from the newly created exception will disable this warning .
Alternatively , you can disable the "" Just my code "" debugging mode ( "" Tools - > Options - > Debugging - > General "" and untick "" Enable Just my code ( Managed only ) "" . This has the sideffect that it will allow you to step into OpenTK code if an error happens . Please , do this only if you are confident in your debugging skills . ";
2007-11-11 21:23:25 +01:00
}
}
2007-10-15 13:12:56 +02:00
}
#endregion
2008-03-01 14:15:31 +01:00
#region public enum GameWindowFlags
/// <summary>Enumerates available GameWindow construction options.</summary>
[Flags]
public enum GameWindowFlags
{
/// <summary>Indicates that the GameWindow should cover the whole screen.</summary>
Fullscreen = 1 ,
}
#endregion
2007-07-23 02:15:18 +02:00
}