2009-06-02 15:49:39 +00:00
#region License
//
// The Open Toolkit Library License
//
// Copyright (c) 2006 - 2009 the Open Toolkit library.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
2009-02-22 10:43:35 +00:00
#endregion
using System ;
using System.Collections.Generic ;
2009-06-02 15:49:39 +00:00
using System.ComponentModel ;
2009-02-22 10:43:35 +00:00
using System.Diagnostics ;
using System.Threading ;
using OpenTK.Graphics ;
2009-06-02 15:49:39 +00:00
using OpenTK.Input ;
using OpenTK.Platform ;
using System.Drawing ;
2009-02-22 10:43:35 +00:00
namespace OpenTK
{
/// <summary>
/// The GameWindow class contains cross-platform methods to create and render on an OpenGL
/// window, handle input and load resources.
/// </summary>
/// <remarks>
/// GameWindow contains several events you can hook or override to add your custom logic:
/// <list>
/// <item>
/// OnLoad: Occurs after creating the OpenGL context, but before entering the main loop.
/// Override to load resources.
/// </item>
/// <item>
/// OnUnload: Occurs after exiting the main loop, but before deleting the OpenGL context.
/// Override to unload resources.
/// </item>
/// <item>
/// OnResize: Occurs whenever GameWindow is resized. You should update the OpenGL Viewport
/// and Projection Matrix here.
/// </item>
/// <item>
/// OnUpdateFrame: Occurs at the specified logic update rate. Override to add your game
/// logic.
/// </item>
/// <item>
/// OnRenderFrame: Occurs at the specified frame render rate. Override to add your
/// rendering code.
/// </item>
/// </list>
/// Call the Run() method to start the application's main loop. Run(double, double) takes two
/// parameters that
/// specify the logic update rate, and the render update rate.
/// </remarks>
2009-06-02 15:49:39 +00:00
public class GameWindow : IGameWindow
2009-02-22 10:43:35 +00:00
{
#region - - - Fields - - -
2009-06-02 15:49:39 +00:00
INativeWindow glWindow ;
2009-02-22 10:43:35 +00:00
//DisplayMode mode;
bool isExiting = false ;
bool hasMainLoop ;
bool disposed ;
double update_period , render_period ;
2009-02-22 15:48:31 +00:00
double target_update_period , target_render_period ;
2009-02-22 10:43:35 +00:00
// 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.
VSyncMode vsync ;
//InputDriver input_driver;
IGraphicsContext glContext ;
2009-02-22 15:48:31 +00:00
int main_loop_thread_id ;
object exit_lock = new object ( ) ;
2009-02-22 10:43:35 +00:00
#endregion
#region - - - Contructors - - -
#region public GameWindow ( )
/// <summary>Constructs a new GameWindow with sensible default attributes..</summary>
public GameWindow ( )
: this ( 640 , 480 , GraphicsMode . Default , "OpenTK Game Window" , 0 , DisplayDevice . Default ) { }
#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 )
/// <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 ) { }
#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
#region public GameWindow ( int width , int height , GraphicsMode mode , string title , GameWindowFlags options )
/// <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 )
2009-03-07 10:49:32 +00:00
: this ( width , height , mode , title , options , device , 1 , 0 , GraphicsContextFlags . Default )
{ }
#endregion
#region public GameWindow ( int width , int height , GraphicsMode mode , string title , GameWindowFlags options , DisplayDevice device , int major , int minor , GraphicsContextFlags flags )
2009-06-02 15:49:39 +00:00
/// <summary>Constructs a new GameWindow with the specified attributes.</summary>
2009-03-07 10:49:32 +00:00
/// <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>
/// <param name="major">The major version for the OpenGL GraphicsContext.</param>
2009-03-25 21:53:12 +00:00
/// <param name="minor">The minor version for the OpenGL GraphicsContext.</param>
2009-03-07 10:49:32 +00:00
/// <param name="flags">The GraphicsContextFlags version for the OpenGL GraphicsContext.</param>
public GameWindow ( int width , int height , GraphicsMode mode , string title , GameWindowFlags options , DisplayDevice device ,
int major , int minor , GraphicsContextFlags flags )
2009-06-02 15:49:39 +00:00
: this ( width , height , mode , title , options , device , major , minor , flags , null )
{ }
#endregion
#region public GameWindow ( int width , int height , GraphicsMode mode , string title , GameWindowFlags options , DisplayDevice device , int major , int minor , GraphicsContextFlags flags , IGraphicsContext sharedContext )
/// <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>
/// <param name="major">The major version for the OpenGL GraphicsContext.</param>
/// <param name="minor">The minor version for the OpenGL GraphicsContext.</param>
/// <param name="flags">The GraphicsContextFlags version for the OpenGL GraphicsContext.</param>
/// <param name="sharedContext">An IGraphicsContext to share resources with.</param>
public GameWindow ( int width , int height , GraphicsMode mode , string title , GameWindowFlags options , DisplayDevice device ,
int major , int minor , GraphicsContextFlags flags , IGraphicsContext sharedContext )
2009-02-22 10:43:35 +00:00
{
2009-06-02 15:49:39 +00:00
if ( width < = 0 )
throw new ArgumentOutOfRangeException ( "width" , "Must be greater than zero." ) ;
if ( height < = 0 )
throw new ArgumentOutOfRangeException ( "height" , "Must be greater than zero." ) ;
2009-02-22 10:43:35 +00:00
if ( mode = = null )
mode = GraphicsMode . Default ;
if ( device = = null )
device = DisplayDevice . Default ;
try
{
2009-06-02 15:49:39 +00:00
Rectangle window_bounds = new Rectangle ( ) ;
window_bounds . X = device . Bounds . Left + ( device . Bounds . Width - width ) / 2 ;
window_bounds . Y = device . Bounds . Top + ( device . Bounds . Height - height ) / 2 ;
window_bounds . Width = width ;
window_bounds . Height = height ;
glWindow = Platform . Factory . Default . CreateNativeWindow (
window_bounds . X , window_bounds . Y ,
window_bounds . Width , window_bounds . Height ,
title , mode , options , device ) ;
glContext = new GraphicsContext ( mode , glWindow . WindowInfo , major , minor , flags ) ;
2009-02-22 10:43:35 +00:00
glContext . MakeCurrent ( this . WindowInfo ) ;
( glContext as IGraphicsContextInternal ) . LoadAll ( ) ;
2009-06-02 15:49:39 +00:00
if ( ( options & GameWindowFlags . Fullscreen ) ! = 0 )
{
device . ChangeResolution ( width , height , mode . ColorFormat . BitsPerPixel , 0 ) ;
this . WindowState = WindowState . Fullscreen ;
}
this . VSync = VSyncMode . On ;
glWindow . Move + = delegate ( object sender , EventArgs e ) { OnMoveInternal ( e ) ; } ;
glWindow . Resize + = delegate ( object sender , EventArgs e ) { OnResizeInternal ( e ) ; } ;
glWindow . Closing + = delegate ( object sender , CancelEventArgs e ) { OnClosingInternal ( e ) ; } ;
glWindow . Closed + = delegate ( object sender , EventArgs e ) { OnClosedInternal ( e ) ; } ;
//glWindow.WindowInfoChanged += delegate(object sender, EventArgs e) { OnWindowInfoChangedInternal(e); };
2009-02-22 10:43:35 +00:00
}
catch ( Exception e )
{
Debug . Print ( e . ToString ( ) ) ;
2009-06-02 15:49:39 +00:00
if ( glWindow ! = null )
glWindow . Dispose ( ) ;
2009-02-22 10:43:35 +00:00
throw ;
}
}
#endregion
2009-03-07 10:49:32 +00:00
#endregion
2009-02-22 10:43:35 +00:00
#region - - - Private Methods - - -
#region void ExitInternal ( )
2009-02-22 15:48:31 +00:00
// Stops the main loop, if one exists.
2009-02-22 10:43:35 +00:00
void ExitInternal ( )
{
if ( HasMainLoop )
{
throw new GameWindowExitException ( ) ;
}
}
2009-02-22 15:48:31 +00:00
#region void ExitAsync ( )
// Gracefully exits the GameWindow. May be called from any thread.
void ExitAsync ( )
{
2009-06-02 15:49:39 +00:00
HasMainLoop = false ;
isExiting = true ;
//UpdateFrame += delegate
//{
// ExitInternal();
//};
2009-02-22 10:43:35 +00:00
}
#endregion
2009-02-22 15:48:31 +00:00
#endregion
2009-02-22 10:43:35 +00: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
2009-06-02 15:49:39 +00:00
#region OnMoveInternal
// Calls OnMove and raises the Move event.
void OnMoveInternal ( EventArgs e )
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
if ( ! this . Exists | | this . IsExiting )
return ;
OnMove ( e ) ;
if ( Move ! = null )
Move ( this , e ) ;
}
#endregion
#region OnResizeInternal
// Calls OnResize and raises the Resize event.
void OnResizeInternal ( EventArgs e )
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
if ( ! this . Exists | | this . IsExiting )
return ;
OnResize ( e ) ;
if ( Resize ! = null )
Resize ( this , e ) ;
}
#endregion
#region OnClosingInternal
void OnClosingInternal ( CancelEventArgs e )
{
OnClosing ( e ) ;
if ( Closing ! = null )
Closing ( this , e ) ;
if ( ! e . Cancel )
ExitAsync ( ) ;
}
#endregion
#region OnClosedInternal
void OnClosedInternal ( EventArgs e )
{
OnClosed ( e ) ;
if ( Closed ! = null )
Closed ( this , e ) ;
}
#endregion
#region OnWindowInfoChangedInternal
void OnWindowInfoChangedInternal ( EventArgs e )
{
glContext . MakeCurrent ( WindowInfo ) ;
OnWindowInfoChanged ( e ) ;
}
#endregion
#region OnUpdateFrameInternal
private void OnUpdateFrameInternal ( FrameEventArgs e )
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
if ( ! this . Exists | | this . IsExiting )
return ;
if ( UpdateFrame ! = null )
UpdateFrame ( this , e ) ;
OnUpdateFrame ( e ) ;
}
#endregion
#region OnRenderFrameInternal
private void OnRenderFrameInternal ( FrameEventArgs e )
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
if ( ! this . Exists | | this . IsExiting )
return ;
if ( RenderFrame ! = null )
RenderFrame ( this , e ) ;
OnRenderFrame ( e ) ;
}
#endregion
#endregion
#region - - - Protected Members - - -
/// <summary>
/// Called when the GameWindow is moved.
/// </summary>
/// <param name="e">Not used.</param>
protected virtual void OnMove ( EventArgs e )
{ }
/// <summary>
/// Called when the GameWindow is resized.
/// </summary>
/// <param name="e">Not used.</param>
protected virtual void OnResize ( EventArgs e )
{ }
/// <summary>
/// Called when the GameWindow is about to close.
/// </summary>
/// <param name="e">
/// The <see cref="System.ComponentModel.CancelEventArgs" /> for this event.
/// Set e.Cancel to true in order to stop the GameWindow from closing.</param>
protected virtual void OnClosing ( CancelEventArgs e )
{ }
/// <summary>
/// Called when the GameWindow has closed.
/// </summary>
/// <param name="e">Not used.</param>
protected virtual void OnClosed ( EventArgs e )
{ }
/// <summary>
/// Called when the WindowInfo for this GameWindow has changed.
/// </summary>
/// <param name="e">Not used.</param>
protected virtual void OnWindowInfoChanged ( EventArgs e )
{ }
2009-02-22 10:43:35 +00:00
#endregion
#region - - - Public Members - - -
#region public virtual void Exit ( )
/// <summary>
2009-02-22 15:48:31 +00:00
/// Gracefully exits the GameWindow. May be called from any thread.
2009-02-22 10:43:35 +00:00
/// </summary>
/// <remarks>
2009-02-22 15:48:31 +00:00
/// <para>Override if you are not using <see cref="GameWindow.Run()"/>.</para>
/// <para>If you override this method, place a call to base.Exit(), to ensure proper OpenTK shutdown.</para>
2009-02-22 10:43:35 +00:00
/// </remarks>
public virtual void Exit ( )
{
2009-02-22 15:48:31 +00:00
lock ( exit_lock )
{
if ( disposed )
2009-06-02 15:49:39 +00:00
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
2009-02-22 10:43:35 +00:00
2009-02-22 15:48:31 +00:00
if ( ! IsExiting & & Exists )
{
CancelEventArgs e = new CancelEventArgs ( ) ;
2009-06-02 15:49:39 +00:00
OnClosingInternal ( e ) ;
2009-02-22 15:48:31 +00:00
if ( e . Cancel )
return ;
2009-06-02 15:49:39 +00:00
isExiting = true ;
2009-02-22 15:48:31 +00:00
if ( HasMainLoop )
{
if ( main_loop_thread_id = = Thread . CurrentThread . ManagedThreadId )
ExitInternal ( ) ;
else
ExitAsync ( ) ;
}
}
}
2009-02-22 10:43:35 +00:00
}
#endregion
#region public IGraphicsContext Context
/// <summary>
/// Returns the opengl IGraphicsContext associated with the current GameWindow.
/// </summary>
public IGraphicsContext Context
{
get
{
2009-06-02 15:49:39 +00:00
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
2009-02-22 10:43:35 +00:00
return glContext ;
}
}
#endregion
#region public bool Exists
/// <summary>
/// Gets a value indicating whether a render window exists.
/// </summary>
public bool Exists
{
get { return glWindow = = null ? false : glWindow . Exists ; }
}
#endregion
#region public string Text
/// <summary>
/// Gets or sets the GameWindow title.
/// </summary>
public string Title
{
get
{
2009-06-02 15:49:39 +00:00
if ( disposed ) throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
2009-02-22 10:43:35 +00:00
return glWindow . Title ;
}
set
{
2009-06-02 15:49:39 +00:00
if ( disposed ) throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
2009-02-22 10:43:35 +00:00
glWindow . Title = value ;
}
}
#endregion
#region public bool Visible
#if false
/// <summary>
/// TODO: This property is not implemented
/// Gets or sets a value indicating whether the GameWindow is visible.
/// </summary>
public bool Visible
{
get
{
throw new NotImplementedException ( ) ;
//return glWindow.Visible;
}
set
{
throw new NotImplementedException ( ) ;
//glWindow.Visible = value;
}
}
#endif
#endregion
#region public IWindowInfo WindowInfo
public IWindowInfo WindowInfo
{
get { if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ; return glWindow . WindowInfo ; }
}
#endregion
#region public void Run ( )
/// <summary>
2009-06-02 15:49:39 +00:00
/// Enters the game loop of the GameWindow using the maximum update rate.
2009-02-22 10:43:35 +00:00
/// </summary>
2009-06-02 15:49:39 +00:00
/// <seealso cref="Run(double)"/>
2009-02-22 10:43:35 +00:00
public void Run ( )
{
2009-06-02 15:49:39 +00:00
if ( disposed ) throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
2009-02-22 10:43:35 +00:00
Run ( 0.0 , 0.0 ) ;
}
#endregion
#region public void Run ( double updateFrequency )
/// <summary>
2009-06-02 15:49:39 +00:00
/// Enters the game loop of the GameWindow using the specified update rate.
2009-02-22 10:43:35 +00:00
/// maximum possible render frequency.
/// </summary>
2009-06-02 15:49:39 +00:00
public void Run ( double updateRate )
2009-02-22 10:43:35 +00:00
{
2009-06-02 15:49:39 +00:00
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
Run ( updateRate , 0.0 ) ;
2009-02-22 10:43:35 +00:00
}
#endregion
#region public void Run ( double updates_per_second , double frames_per_second )
/// <summary>
/// Enters the game loop of the GameWindow updating and rendering at the specified frequency.
/// </summary>
/// <param name="updates_per_second">The frequency of UpdateFrame events.</param>
/// <param name="frames_per_second">The frequency of RenderFrame events.</param>
public void Run ( double updates_per_second , double frames_per_second )
{
2009-02-22 15:48:31 +00:00
if ( disposed )
2009-06-02 15:49:39 +00:00
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
2009-02-22 15:48:31 +00:00
2009-02-22 10:43:35 +00:00
try
{
2009-02-22 15:48:31 +00:00
// Necessary to be here, otherwise Exit() wouldn't work correctly when called inside OnLoad().
hasMainLoop = true ;
main_loop_thread_id = Thread . CurrentThread . ManagedThreadId ;
2009-02-22 10:43:35 +00:00
if ( updates_per_second < 0.0 | | updates_per_second > 200.0 )
2009-02-22 15:48:31 +00:00
throw new ArgumentOutOfRangeException ( "updates_per_second" , updates_per_second ,
"Parameter should be inside the range [0.0, 200.0]" ) ;
2009-02-22 10:43:35 +00:00
if ( frames_per_second < 0.0 | | frames_per_second > 200.0 )
2009-02-22 15:48:31 +00:00
throw new ArgumentOutOfRangeException ( "frames_per_second" , frames_per_second ,
"Parameter should be inside the range [0.0, 200.0]" ) ;
2009-02-22 10:43:35 +00:00
TargetUpdateFrequency = updates_per_second ;
TargetRenderFrequency = frames_per_second ;
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 ;
2009-06-02 15:49:39 +00:00
FrameEventArgs update_args = new FrameEventArgs ( ) ;
FrameEventArgs render_args = new FrameEventArgs ( ) ;
2009-02-22 10:43:35 +00:00
update_watch . Reset ( ) ;
render_watch . Reset ( ) ;
2009-02-22 15:48:31 +00:00
OnLoadInternal ( EventArgs . Empty ) ;
2009-06-02 15:49:39 +00:00
OnResizeInternal ( EventArgs . Empty ) ;
2009-02-22 10:43:35 +00:00
Debug . Print ( "Entering main loop." ) ;
2009-06-02 15:49:39 +00:00
while ( ! IsExiting & & HasMainLoop )
2009-02-22 10:43:35 +00:00
{
ProcessEvents ( ) ;
// 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 ;
if ( next_update < - 1.0 ) // Cap the maximum time drift, to avoid lengthy catch-up games.
next_update = - 1.0 ;
update_time_counter + = time ;
+ + num_updates ;
update_watch . Reset ( ) ;
update_watch . Start ( ) ;
2009-06-02 15:49:39 +00:00
if ( time > 0 )
{
update_args . Time = time ;
OnUpdateFrameInternal ( update_args ) ;
update_time = update_watch . Elapsed . TotalSeconds ;
}
2009-02-22 10:43:35 +00:00
if ( TargetUpdateFrequency = = 0.0 )
break ;
time = update_watch . Elapsed . TotalSeconds ;
next_update - = time ;
update_time_counter + = time ;
// Allow up to 10 frames to be dropped. Prevents the application from hanging
// with very high update frequencies.
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
if ( TargetRenderPeriod ! = 0 & & RenderTime > 2.0 * TargetRenderPeriod )
Context . VSync = false ;
else
Context . VSync = true ;
}
if ( time_left < = 0.0 )
{
next_render = time_left + TargetRenderPeriod ;
if ( next_render < - 1.0 ) // Cap the maximum time drift, to avoid lengthy catch-up games.
next_render = - 1.0 ;
render_watch . Reset ( ) ;
render_watch . Start ( ) ;
2009-06-02 15:49:39 +00:00
if ( time > 0 )
{
render_period = render_args . Time = time ;
OnRenderFrameInternal ( render_args ) ;
render_time = render_watch . Elapsed . TotalSeconds ;
}
2009-02-22 10:43:35 +00:00
}
}
}
catch ( GameWindowExitException )
{
Debug . WriteLine ( "GameWindowExitException caught - exiting main loop." ) ;
}
finally
{
Debug . Print ( "Restoring priority." ) ;
Thread . CurrentThread . Priority = ThreadPriority . Normal ;
OnUnloadInternal ( EventArgs . Empty ) ;
if ( Exists )
{
glContext . Dispose ( ) ;
glContext = null ;
2009-06-02 15:49:39 +00:00
glWindow . Dispose ( ) ;
while ( this . Exists )
this . ProcessEvents ( ) ;
glWindow = null ;
2009-02-22 10:43:35 +00: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>
public void ProcessEvents ( )
{
2009-06-02 15:49:39 +00:00
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
2009-02-22 10:43:35 +00:00
glWindow . ProcessEvents ( ) ;
}
#endregion
2009-06-02 15:49:39 +00:00
#region OnRenderFrame
2009-02-22 10:43:35 +00:00
/// <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>
2009-06-02 15:49:39 +00:00
protected virtual void OnRenderFrame ( FrameEventArgs e )
2009-02-22 10:43:35 +00:00
{
}
/// <summary>
2009-06-02 15:49:39 +00:00
/// Occurs when it is time to render a frame.
2009-02-22 10:43:35 +00:00
/// </summary>
2009-06-02 15:49:39 +00:00
public event EventHandler < FrameEventArgs > RenderFrame ;
2009-02-22 10:43:35 +00:00
#endregion
2009-06-02 15:49:39 +00:00
#region OnUpdateFrame
2009-02-22 10:43:35 +00:00
/// <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>
2009-06-02 15:49:39 +00:00
protected virtual void OnUpdateFrame ( FrameEventArgs e )
2009-02-22 10:43:35 +00:00
{
}
/// <summary>
2009-06-02 15:49:39 +00:00
/// Occurs when it is time to update a frame.
2009-02-22 10:43:35 +00:00
/// </summary>
2009-06-02 15:49:39 +00:00
public event EventHandler < FrameEventArgs > UpdateFrame ;
2009-02-22 10:43:35 +00:00
#endregion
2009-06-02 15:49:39 +00:00
#region OnLoad
2009-02-22 10:43:35 +00:00
/// <summary>
/// Raises the Load event, and calls the user's OnLoad override.
/// </summary>
/// <param name="e"></param>
private void OnLoadInternal ( EventArgs e )
{
2009-06-02 15:49:39 +00:00
Debug . Print ( "{0}.Load" , this . GetType ( ) . Name ) ;
2009-02-22 10:43:35 +00:00
Debug . WriteLine ( String . Format ( "OpenGL driver information: {0}, {1}, {2}" ,
GL . GetString ( StringName . Renderer ) ,
GL . GetString ( StringName . Vendor ) ,
GL . GetString ( StringName . Version ) ) ) ;
2009-06-02 15:49:39 +00:00
OnResizeInternal ( EventArgs . Empty ) ;
Load ( this , e ) ;
2009-02-22 10:43:35 +00:00
OnLoad ( e ) ;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="e">Not used.</param>
public virtual void OnLoad ( EventArgs e )
{
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
}
#endregion
2009-06-02 15:49:39 +00:00
#region OnUnload
2009-02-22 10:43:35 +00:00
/// <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 )
{
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
}
#endregion
#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
/// OpenTK.Graphics.OpenGL functions or properties.
/// </summary>
public bool IsExiting
{
2009-06-02 15:49:39 +00:00
get
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
return isExiting ;
}
2009-02-22 10:43:35 +00:00
}
#endregion
#region public Keyboard Keyboard
/// <summary>
/// Gets the primary Keyboard device, or null if no Keyboard exists.
/// </summary>
2009-06-02 15:49:39 +00:00
[Obsolete]
2009-02-22 10:43:35 +00:00
public KeyboardDevice Keyboard
{
get
{
2009-06-02 15:49:39 +00:00
if ( InputDriver . Keyboard . Count > 0 )
return InputDriver . Keyboard [ 0 ] ;
2009-02-22 10:43:35 +00:00
else
return null ;
}
}
#endregion
#region public Mouse Mouse
/// <summary>
/// Gets the primary Mouse device, or null if no Mouse exists.
/// </summary>
2009-06-02 15:49:39 +00:00
[Obsolete]
2009-02-22 10:43:35 +00:00
public MouseDevice Mouse
{
get
{
2009-06-02 15:49:39 +00:00
if ( InputDriver . Mouse . Count > 0 )
return InputDriver . Mouse [ 0 ] ;
2009-02-22 10:43:35 +00:00
else
return null ;
}
}
#endregion
2009-03-01 00:28:31 +00:00
#region public IList < JoystickDevice > Joysticks
/// <summary>
/// Gets a readonly IList containing all available OpenTK.Input.JoystickDevices.
/// </summary>
2009-06-02 15:49:39 +00:00
[Obsolete]
2009-03-01 00:28:31 +00:00
public IList < JoystickDevice > Joysticks
{
2009-06-02 15:49:39 +00:00
get { return InputDriver . Joysticks ; }
2009-03-01 00:28:31 +00:00
}
#endregion
2009-02-22 10:43:35 +00:00
#region public VSyncMode VSync
/// <summary>
/// Gets or sets the VSyncMode.
/// </summary>
public VSyncMode VSync
{
get
{
2009-06-02 15:49:39 +00:00
if ( disposed )
throw new ObjectDisposedException ( "GameWindow" ) ;
GraphicsContext . Assert ( ) ;
2009-02-22 10:43:35 +00:00
return vsync ;
}
set
{
2009-06-02 15:49:39 +00:00
if ( disposed )
throw new ObjectDisposedException ( "GameWindow" ) ;
GraphicsContext . Assert ( ) ;
2009-02-22 10:43:35 +00:00
if ( value = = VSyncMode . Off )
Context . VSync = false ;
else
Context . VSync = true ;
vsync = value ;
}
}
#endregion
2009-06-02 15:49:39 +00:00
#region MakeCurrent
/// <summary>
/// Makes the GraphicsContext current on the calling thread.
/// </summary>
public void MakeCurrent ( )
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
Context . MakeCurrent ( WindowInfo ) ;
}
#endregion
2009-02-22 10:43:35 +00:00
#region public void SwapBuffers ( )
/// <summary>
/// Swaps the front and back buffer, presenting the rendered scene to the user.
/// </summary>
public void SwapBuffers ( )
{
2009-06-02 15:49:39 +00:00
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
2009-02-22 10:43:35 +00:00
this . Context . SwapBuffers ( ) ;
}
#endregion
#region public WindowState WindowState
/// <summary>
/// Gets or states the state of the GameWindow.
/// </summary>
public WindowState WindowState
{
get
{
return glWindow . WindowState ;
}
set
{
glWindow . WindowState = value ;
}
}
#endregion
#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
#endregion
2009-06-02 15:49:39 +00:00
#region - - - INativeWindow Members - - -
#region Icon
2009-02-22 15:48:31 +00:00
/// <summary>
2009-06-02 15:49:39 +00:00
/// Gets or sets the System.Drawing.Icon for this GameWindow.
2009-02-22 15:48:31 +00:00
/// </summary>
2009-06-02 15:49:39 +00:00
public Icon Icon
{
get
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
2009-02-22 15:48:31 +00:00
2009-06-02 15:49:39 +00:00
return glWindow . Icon ;
}
set
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
2009-02-22 15:48:31 +00:00
2009-06-02 15:49:39 +00:00
glWindow . Icon = value ;
}
}
#endregion
#region Focused
/// <summary>
/// Gets a System.Boolean that indicates whether this GameWindow has input focus.
/// </summary>
public bool Focused
{
get
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
return glWindow . Focused ;
}
}
#endregion
#region Visible
/// <summary>
/// Gets or sets a System.Boolean that indicates whether this GameWindow is visible.
/// </summary>
public bool Visible
{
get
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
return glWindow . Visible ;
}
set
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
glWindow . Visible = value ;
}
}
#endregion
#region Bounds
/// <summary>
/// Gets or sets a <see cref="System.Drawing.Rectangle"/> structure the contains the external bounds of this window, in screen coordinates.
/// External bounds include the title bar, borders and drawing area of the window.
/// </summary>
public Rectangle Bounds
{
get
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
return glWindow . Bounds ;
}
set
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
glWindow . Bounds = value ;
}
}
#endregion
#region Location
/// <summary>
2009-06-25 11:16:45 +00:00
/// Gets or sets a <see cref="System.Drawing.Point"/> structure that contains the location of this window on the desktop.
2009-06-02 15:49:39 +00:00
/// </summary>
public Point Location
{
get
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
return glWindow . Location ;
}
set
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
glWindow . Location = value ;
}
}
#endregion
#region Size
/// <summary>
2009-06-25 11:16:45 +00:00
/// Gets or sets a <see cref="System.Drawing.Size"/> structure that contains the external size of this window.
2009-06-02 15:49:39 +00:00
/// </summary>
public Size Size
{
get
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
return glWindow . Size ;
}
set
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
glWindow . Size = value ;
}
}
#endregion
#region X
/// <summary>
/// Gets or sets the horizontal location of this window on the desktop.
/// </summary>
public int X
{
get
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
return glWindow . X ;
}
set
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
glWindow . X = value ;
}
}
#endregion
#region Y
/// <summary>
/// Gets or sets the vertical location of this window on the desktop.
/// </summary>
public int Y
{
get
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
return glWindow . Y ;
}
set
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
glWindow . Y = value ;
}
}
#endregion
#region Width
/// <summary>
/// Gets or sets the external width of this window.
/// </summary>
public int Width
{
get
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
return glWindow . Width ;
}
set
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
glWindow . Width = value ;
}
}
#endregion
#region Height
/// <summary>
/// Gets or sets the external height of this window.
/// </summary>
public int Height
{
get
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
return glWindow . Height ;
}
set
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
glWindow . Height = value ;
}
}
#endregion
#region ClientRectangle
/// <summary>
/// Gets or sets a <see cref="System.Drawing.Rectangle"/> structure that contains the internal bounds of this window, in client coordinates.
/// The internal bounds include the drawing area of the window, but exclude the titlebar and window borders.
/// </summary>
public Rectangle ClientRectangle
{
get
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
return glWindow . ClientRectangle ;
}
set
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
glWindow . ClientRectangle = value ;
}
}
#endregion
#region ClientSize
/// <summary>
/// Gets or sets a <see cref="System.Drawing.Size"/> structure that contains the internal size this window.
/// </summary>
public Size ClientSize
{
get
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
return glWindow . ClientSize ;
}
set
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
glWindow . ClientSize = value ;
}
}
#endregion
#region InputDriver
2009-06-28 10:49:10 +00:00
/// <summary>
/// This property is deprecated.
/// </summary>
2009-06-02 15:49:39 +00:00
[Obsolete]
public IInputDriver InputDriver
{
get
{
if ( disposed )
throw new ObjectDisposedException ( this . GetType ( ) . Name ) ;
return glWindow . InputDriver ;
}
}
#endregion
#region Close
2009-06-28 10:49:10 +00:00
/// <summary>
/// Closes the GameWindow. Equivalent to calling <see cref="OpenTK.GameWindow.Exit()"/>.
/// </summary>
2009-06-02 15:49:39 +00:00
public void Close ( )
{
Exit ( ) ;
}
#endregion
#region PointToClient
/// <summary>
/// Transforms the specified point from screen to client coordinates.
/// </summary>
/// <param name="point">
/// A <see cref="System.Drawing.Point"/> to transform.
/// </param>
/// <returns>
/// The point transformed to client coordinates.
/// </returns>
public System . Drawing . Point PointToClient ( System . Drawing . Point point )
{
return glWindow . PointToClient ( point ) ;
}
#endregion
#region PointToScreen
/// <summary>
/// Transforms the specified point from client to screen coordinates.
/// </summary>
/// <param name="point">
/// A <see cref="System.Drawing.Point"/> to transform.
/// </param>
/// <returns>
/// The point transformed to screen coordinates.
/// </returns>
2009-06-25 11:16:45 +00:00
public System . Drawing . Point PointToScreen ( System . Drawing . Point point )
2009-06-02 15:49:39 +00:00
{
// Here we use the fact that PointToClient just translates the point, and PointToScreen
// should perform the inverse operation.
System . Drawing . Point trans = PointToClient ( System . Drawing . Point . Empty ) ;
2009-06-25 11:16:45 +00:00
point . X - = trans . X ;
point . Y - = trans . Y ;
return point ;
2009-06-02 15:49:39 +00:00
}
#endregion
#region Events
/// <summary>
/// Occurs before the window is displayed for the first time.
/// </summary>
public event EventHandler < EventArgs > Load = delegate { } ;
/// <summary>
/// Occurs before the window is destroyed.
/// </summary>
public event EventHandler < EventArgs > Unload = delegate { } ;
/// <summary>
/// Occurs whenever the window is moved.
/// </summary>
public event EventHandler < EventArgs > Move = delegate { } ;
/// <summary>
/// Occurs whenever the window is resized.
/// </summary>
public event EventHandler < EventArgs > Resize = delegate { } ;
/// <summary>
/// Occurs when the window is about to close.
/// </summary>
public event EventHandler < CancelEventArgs > Closing = delegate { } ;
/// <summary>
/// Occurs after the window has closed.
/// </summary>
public event EventHandler < EventArgs > Closed = delegate { } ;
/// <summary>
/// Occurs when the window is disposed.
/// </summary>
public event EventHandler < EventArgs > Disposed = delegate { } ;
/// <summary>
/// Occurs when the <see cref="Icon"/> property of the window changes.
/// </summary>
public event EventHandler < EventArgs > IconChanged = delegate { } ;
/// <summary>
/// Occurs when the <see cref="Title"/> property of the window changes.
/// </summary>
public event EventHandler < EventArgs > TitleChanged = delegate { } ;
/// <summary>
/// Occurs when the <see cref="Visible"/> property of the window changes.
/// </summary>
public event EventHandler < EventArgs > VisibleChanged = delegate { } ;
/// <summary>
/// Occurs when the <see cref="Focused"/> property of the window changes.
/// </summary>
public event EventHandler < EventArgs > FocusedChanged = delegate { } ;
#endregion
#endregion
#region - - - GameWindow Timing - - -
2009-02-22 10:43:35 +00: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
#region public double TargetRenderPeriod
/// <summary>
/// Gets or sets a double representing the target render period, in seconds.
/// </summary>
2009-03-25 21:53:12 +00:00
/// <remarks>
2009-02-22 10:43:35 +00:00
/// <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
{
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
return target_render_period ;
}
set
{
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
if ( value < = 0.005 )
{
2009-02-22 15:48:31 +00:00
target_render_period = 0.0 ;
2009-02-22 10:43:35 +00:00
}
else if ( value < = 1.0 )
{
target_render_period = value ;
}
else Debug . Print ( "Target render period clamped to 1.0 seconds." ) ;
}
}
#endregion
#region public double TargetRenderFrequency
/// <summary>
/// Gets or sets a double representing the target render frequency, in Herz.
/// </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
{
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
if ( TargetRenderPeriod = = 0.0 )
return 0.0 ;
return 1.0 / TargetRenderPeriod ;
}
set
{
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
if ( value < 1.0 )
{
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>
/// Gets or sets a double representing the target update period, in seconds.
/// </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
{
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
return target_update_period ;
}
set
{
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
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>
/// Gets or sets a double representing the target update frequency, in Herz.
/// </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
{
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
if ( TargetUpdatePeriod = = 0.0 )
return 0.0 ;
return 1.0 / TargetUpdatePeriod ;
}
set
{
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
if ( value < 1.0 )
{
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>
/// Gets a double representing the actual frequency of RenderFrame events, in Herz (i.e. FPS or Frames Per Second).
/// </summary>
public double RenderFrequency
{
get
{
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
if ( render_period = = 0.0 )
return 1.0 ;
return 1.0 / render_period ;
}
}
#endregion
#region public double RenderPeriod
/// <summary>
/// Gets a double representing the period of RenderFrame events, in seconds.
/// </summary>
public double RenderPeriod
{
get
{
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
return render_period ;
}
}
#endregion
#region public double UpdateFrequency
/// <summary>
/// Gets a double representing the frequency of UpdateFrame events, in Herz.
/// </summary>
public double UpdateFrequency
{
get
{
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
if ( update_period = = 0.0 )
return 1.0 ;
return 1.0 / update_period ;
}
}
#endregion
#region public double UpdatePeriod
/// <summary>
/// Gets a double representing the period of UpdateFrame events, in seconds.
/// </summary>
public double UpdatePeriod
{
get
{
if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ;
return update_period ;
}
}
#endregion
#region public double RenderTime
/// <summary>
/// Gets a double representing the time spent in the RenderFrame function, in seconds.
/// </summary>
public double RenderTime
{
get { if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ; return render_time ; }
protected set { if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ; render_time = value ; }
}
#endregion
#region public double RenderTime
/// <summary>
/// Gets a double representing the time spent in the UpdateFrame function, in seconds.
/// </summary>
public double UpdateTime
{
get { if ( disposed ) throw new ObjectDisposedException ( "GameWindow" ) ; return update_time ; }
}
#endregion
#endregion
#region - - - IDisposable Members - - -
/// <summary>
/// Disposes of the GameWindow, releasing all resources consumed by it.
/// </summary>
public void Dispose ( )
{
try
{
Dispose ( true ) ;
}
finally
{
DisposeInternal ( true ) ;
}
GC . SuppressFinalize ( this ) ;
}
private void DisposeInternal ( bool manual )
{
if ( ! disposed )
{
if ( manual )
{
if ( glContext ! = null )
{
glContext . Dispose ( ) ;
glContext = null ;
}
if ( glWindow ! = null )
{
glWindow . Dispose ( ) ;
glWindow = null ;
}
}
disposed = true ;
}
}
/// <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>
//~GameWindow()
//{
// Dispose(false);
// DisposeInternal(false);
//}
#endregion
}
#region public enum VSyncMode
/// <summary>
/// Enumerates available VSync modes.
/// </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>
Adaptive ,
}
#endregion
#region - - - GameWindow Exceptions - - -
[DebuggerNonUserCode]
class GameWindowExitException : ApplicationException
{
public override string Message
{
get
{
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 . ";
}
}
}
#endregion
#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
}