2014-01-07 08:52:02 +01:00
#region License
2009-06-02 17:49:39 +02:00
//
// 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 11:43:35 +01:00
#endregion
using System ;
using System.Collections.Generic ;
2009-06-02 17:49:39 +02:00
using System.ComponentModel ;
2009-02-22 11:43:35 +01:00
using System.Diagnostics ;
2011-12-08 01:03:14 +01:00
#if ! MINIMAL
2009-08-21 00:22:38 +02:00
using System.Drawing ;
2011-12-08 01:03:14 +01:00
#endif
2009-02-22 11:43:35 +01:00
using System.Threading ;
using OpenTK.Graphics ;
2009-06-02 17:49:39 +02:00
using OpenTK.Input ;
using OpenTK.Platform ;
2009-02-22 11:43:35 +01: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-08-21 21:34:20 +02:00
public class GameWindow : NativeWindow , IGameWindow , IDisposable
2009-02-22 11:43:35 +01:00
{
#region - - - Fields - - -
2014-01-14 14:20:38 +01:00
const double MaxFrequency = 500.0 ; // Frequency cap for Update/RenderFrame events
2014-01-07 08:52:02 +01:00
readonly Stopwatch watch = new Stopwatch ( ) ;
2014-01-14 23:53:24 +01:00
readonly IJoystickDriver LegacyJoystick =
Factory . Default . CreateLegacyJoystickDriver ( ) ;
2009-08-21 00:22:38 +02:00
IGraphicsContext glContext ;
2009-02-22 11:43:35 +01:00
2009-08-21 00:22:38 +02:00
bool isExiting = false ;
2009-02-22 11:43:35 +01:00
double update_period , render_period ;
2009-02-22 16:48:31 +01:00
double target_update_period , target_render_period ;
2014-01-07 22:09:02 +01:00
double update_time ; // length of last UpdateFrame event
double render_time ; // length of last RenderFrame event
2014-01-07 08:52:02 +01:00
double update_timestamp ; // timestamp of last UpdateFrame event
double render_timestamp ; // timestamp of last RenderFrame event
2014-01-14 13:04:30 +01:00
double update_epsilon ; // quantization error for UpdateFrame events
2014-01-14 13:55:24 +01:00
bool is_running_slowly ; // true, when UpdatePeriod cannot reach TargetUpdatePeriod
2009-02-22 11:43:35 +01:00
VSyncMode vsync ;
2009-11-17 15:54:30 +01:00
FrameEventArgs update_args = new FrameEventArgs ( ) ;
FrameEventArgs render_args = new FrameEventArgs ( ) ;
2009-02-22 11:43:35 +01:00
#endregion
#region - - - Contructors - - -
#region public GameWindow ( )
2009-08-21 00:22:38 +02:00
/// <summary>Constructs a new GameWindow with sensible default attributes.</summary>
2009-02-22 11:43:35 +01:00
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 11:49:32 +01: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-08-21 00:22:38 +02:00
/// <summary>Constructs a new GameWindow with the specified attributes.</summary>
2009-03-07 11:49:32 +01: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 22:53:12 +01:00
/// <param name="minor">The minor version for the OpenGL GraphicsContext.</param>
2009-03-07 11:49:32 +01: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 17:49:39 +02: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 ,
2009-08-21 00:22:38 +02:00
int major , int minor , GraphicsContextFlags flags , IGraphicsContext sharedContext )
: base ( width , height , title , options ,
mode = = null ? GraphicsMode . Default : mode ,
device = = null ? DisplayDevice . Default : device )
2009-02-22 11:43:35 +01:00
{
try
{
2009-08-21 00:22:38 +02:00
glContext = new GraphicsContext ( mode = = null ? GraphicsMode . Default : mode , WindowInfo , major , minor , flags ) ;
glContext . MakeCurrent ( WindowInfo ) ;
2009-02-22 11:43:35 +01:00
( glContext as IGraphicsContextInternal ) . LoadAll ( ) ;
2009-06-02 17:49:39 +02:00
2009-08-21 00:22:38 +02:00
VSync = VSyncMode . On ;
2009-06-02 17:49:39 +02:00
//glWindow.WindowInfoChanged += delegate(object sender, EventArgs e) { OnWindowInfoChangedInternal(e); };
2009-02-22 11:43:35 +01:00
}
catch ( Exception e )
{
Debug . Print ( e . ToString ( ) ) ;
2009-08-21 00:22:38 +02:00
base . Dispose ( ) ;
2009-02-22 11:43:35 +01:00
throw ;
}
}
2009-06-02 17:49:39 +02:00
#endregion
#endregion
2009-08-21 00:22:38 +02:00
#region - - - Public Members - - -
2009-06-02 17:49:39 +02:00
2009-08-21 00:22:38 +02:00
#region Methods
2009-06-02 17:49:39 +02:00
2009-08-21 00:22:38 +02:00
#region Dispose
2009-06-02 17:49:39 +02:00
/// <summary>
2009-08-21 00:22:38 +02:00
/// Disposes of the GameWindow, releasing all resources consumed by it.
2009-06-02 17:49:39 +02:00
/// </summary>
2009-09-04 23:27:05 +02:00
public override void Dispose ( )
2009-08-21 00:22:38 +02:00
{
try
{
Dispose ( true ) ;
}
finally
{
2009-09-04 23:27:05 +02:00
try
2009-08-21 00:22:38 +02:00
{
if ( glContext ! = null )
{
glContext . Dispose ( ) ;
glContext = null ;
}
2009-09-04 23:27:05 +02:00
}
finally
{
2009-08-21 00:22:38 +02:00
base . Dispose ( ) ;
}
}
GC . SuppressFinalize ( this ) ;
}
2009-08-17 11:34:15 +02:00
2009-02-22 11:43:35 +01:00
#endregion
2009-08-21 00:22:38 +02:00
#region Exit
2009-02-22 11:43:35 +01:00
/// <summary>
2009-11-04 21:48:02 +01:00
/// Closes the GameWindow. Equivalent to <see cref="NativeWindow.Close"/> method.
2009-02-22 11:43:35 +01:00
/// </summary>
/// <remarks>
2009-02-22 16:48:31 +01: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 11:43:35 +01:00
/// </remarks>
public virtual void Exit ( )
{
2009-08-21 00:26:31 +02:00
Close ( ) ;
2009-02-22 11:43:35 +01:00
}
#endregion
2009-08-21 00:22:38 +02:00
#region MakeCurrent
2009-02-22 11:43:35 +01:00
/// <summary>
2009-08-21 00:22:38 +02:00
/// Makes the GraphicsContext current on the calling thread.
2009-02-22 11:43:35 +01:00
/// </summary>
2009-08-21 00:22:38 +02:00
public void MakeCurrent ( )
2009-02-22 11:43:35 +01:00
{
2009-08-21 00:22:38 +02:00
EnsureUndisposed ( ) ;
Context . MakeCurrent ( WindowInfo ) ;
2009-02-22 11:43:35 +01:00
}
2010-02-03 20:04:42 +01:00
#endregion
#region OnClose
/// <summary>
/// Called when the NativeWindow 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>
2010-03-11 23:53:11 +01:00
protected override void OnClosing ( System . ComponentModel . CancelEventArgs e )
2010-02-03 20:04:42 +01:00
{
base . OnClosing ( e ) ;
if ( ! e . Cancel )
{
isExiting = true ;
OnUnloadInternal ( EventArgs . Empty ) ;
}
}
2009-02-22 11:43:35 +01:00
#endregion
2009-08-21 00:22:38 +02:00
#region OnLoad
2009-02-22 11:43:35 +01:00
/// <summary>
2009-09-04 23:27:05 +02:00
/// Called after an OpenGL context has been established, but before entering the main loop.
2009-02-22 11:43:35 +01:00
/// </summary>
2009-08-21 00:22:38 +02:00
/// <param name="e">Not used.</param>
2009-10-21 15:33:00 +02:00
protected virtual void OnLoad ( EventArgs e )
2009-02-22 11:43:35 +01:00
{
2010-10-28 11:37:57 +02:00
Load ( this , e ) ;
2009-02-22 11:43:35 +01:00
}
#endregion
2009-08-21 00:22:38 +02:00
#region OnUnload
2009-02-22 11:43:35 +01:00
/// <summary>
2009-09-04 23:27:05 +02:00
/// Called after GameWindow.Exit was called, but before destroying the OpenGL context.
2009-02-22 11:43:35 +01:00
/// </summary>
2009-08-21 00:22:38 +02:00
/// <param name="e">Not used.</param>
2009-10-21 15:33:00 +02:00
protected virtual void OnUnload ( EventArgs e )
2009-02-22 11:43:35 +01:00
{
2010-10-28 11:37:57 +02:00
Unload ( this , e ) ;
2009-02-22 11:43:35 +01:00
}
#endregion
#region public void Run ( )
/// <summary>
2009-06-02 17:49:39 +02:00
/// Enters the game loop of the GameWindow using the maximum update rate.
2009-02-22 11:43:35 +01:00
/// </summary>
2009-06-02 17:49:39 +02:00
/// <seealso cref="Run(double)"/>
2009-02-22 11:43:35 +01:00
public void Run ( )
{
Run ( 0.0 , 0.0 ) ;
}
#endregion
#region public void Run ( double updateFrequency )
/// <summary>
2009-06-02 17:49:39 +02:00
/// Enters the game loop of the GameWindow using the specified update rate.
2009-02-22 11:43:35 +01:00
/// maximum possible render frequency.
/// </summary>
2009-06-02 17:49:39 +02:00
public void Run ( double updateRate )
2009-02-22 11:43:35 +01:00
{
2009-06-02 17:49:39 +02:00
Run ( updateRate , 0.0 ) ;
2009-02-22 11:43:35 +01: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>
2009-08-21 00:22:38 +02:00
/// <remarks>
/// When overriding the default game loop 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>
2009-02-22 11:43:35 +01:00
/// <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-08-21 00:22:38 +02:00
EnsureUndisposed ( ) ;
2009-02-22 16:48:31 +01:00
2009-02-22 11:43:35 +01:00
try
{
if ( updates_per_second < 0.0 | | updates_per_second > 200.0 )
2009-02-22 16:48:31 +01:00
throw new ArgumentOutOfRangeException ( "updates_per_second" , updates_per_second ,
"Parameter should be inside the range [0.0, 200.0]" ) ;
2009-02-22 11:43:35 +01:00
if ( frames_per_second < 0.0 | | frames_per_second > 200.0 )
2009-02-22 16:48:31 +01:00
throw new ArgumentOutOfRangeException ( "frames_per_second" , frames_per_second ,
"Parameter should be inside the range [0.0, 200.0]" ) ;
2009-02-22 11:43:35 +01:00
TargetUpdateFrequency = updates_per_second ;
TargetRenderFrequency = frames_per_second ;
2009-09-19 23:56:13 +02:00
Visible = true ; // Make sure the GameWindow is visible.
2009-02-22 16:48:31 +01:00
OnLoadInternal ( EventArgs . Empty ) ;
2010-03-11 23:53:11 +01:00
OnResize ( EventArgs . Empty ) ;
2009-02-22 11:43:35 +01:00
2009-10-07 17:18:53 +02:00
// On some platforms, ProcessEvents() does not return while the user is resizing or moving
// the window. We can avoid this issue by raising UpdateFrame and RenderFrame events
// whenever we encounter a size or move event.
2013-09-27 14:41:37 +02:00
// Note: hack disabled. Threaded rendering provides a better solution to this issue.
2010-10-02 20:52:34 +02:00
//Move += DispatchUpdateAndRenderFrame;
//Resize += DispatchUpdateAndRenderFrame;
2009-10-07 17:18:53 +02:00
2009-02-22 11:43:35 +01:00
Debug . Print ( "Entering main loop." ) ;
2014-01-06 01:57:54 +01:00
watch . Start ( ) ;
2010-02-03 20:04:42 +01:00
while ( true )
2009-02-22 11:43:35 +01:00
{
ProcessEvents ( ) ;
2010-02-03 20:04:42 +01:00
if ( Exists & & ! IsExiting )
DispatchUpdateAndRenderFrame ( this , EventArgs . Empty ) ;
else
return ;
2009-02-22 11:43:35 +01:00
}
}
finally
{
2010-02-03 20:04:42 +01:00
Move - = DispatchUpdateAndRenderFrame ;
Resize - = DispatchUpdateAndRenderFrame ;
2009-02-22 11:43:35 +01:00
if ( Exists )
{
2010-02-03 20:04:42 +01:00
// TODO: Should similar behaviour be retained, possibly on native window level?
//while (this.Exists)
// ProcessEvents(false);
2009-02-22 11:43:35 +01:00
}
2009-10-07 15:08:13 +02:00
}
}
2014-01-13 11:36:56 +01:00
double ClampElapsed ( double elapsed )
{
return MathHelper . Clamp ( elapsed , 0.0 , 1.0 ) ;
}
2014-01-06 01:57:54 +01:00
void DispatchUpdateAndRenderFrame ( object sender , EventArgs e )
2009-10-07 15:08:13 +02:00
{
2014-01-14 13:55:24 +01:00
int is_running_slowly_retries = 4 ;
2014-01-07 08:52:02 +01:00
double timestamp = watch . Elapsed . TotalSeconds ;
2014-01-14 13:04:30 +01:00
double elapsed = 0 ;
2014-01-13 11:22:33 +01:00
2014-01-14 13:04:30 +01:00
elapsed = ClampElapsed ( timestamp - update_timestamp ) ;
while ( elapsed > 0 & & elapsed + update_epsilon > = TargetUpdatePeriod )
2011-11-03 11:34:57 +01:00
{
2014-01-14 13:04:30 +01:00
RaiseUpdateFrame ( elapsed , ref timestamp ) ;
// Calculate difference (positive or negative) between
// actual elapsed time and target elapsed time. We must
// compensate for this difference.
update_epsilon + = elapsed - TargetUpdatePeriod ;
// Prepare for next loop
2014-01-13 11:36:56 +01:00
elapsed = ClampElapsed ( timestamp - update_timestamp ) ;
2014-01-14 13:33:41 +01:00
if ( TargetUpdatePeriod < = Double . Epsilon )
{
// According to the TargetUpdatePeriod documentation,
// a TargetUpdatePeriod of zero means we will raise
// UpdateFrame events as fast as possible (one event
// per ProcessEvents() call)
break ;
}
2014-01-14 13:55:24 +01:00
is_running_slowly = update_epsilon > = TargetUpdatePeriod ;
if ( is_running_slowly & & - - is_running_slowly_retries = = 0 )
{
// If UpdateFrame consistently takes longer than TargetUpdateFrame
// stop raising events to avoid hanging inside the UpdateFrame loop.
break ;
}
2014-01-13 11:22:33 +01:00
}
2014-01-07 08:52:02 +01:00
2014-01-13 11:36:56 +01:00
elapsed = ClampElapsed ( timestamp - render_timestamp ) ;
2014-01-13 11:22:33 +01:00
if ( elapsed > 0 & & elapsed > = TargetRenderPeriod )
2014-01-07 08:52:02 +01:00
{
2014-01-13 11:22:33 +01:00
RaiseRenderFrame ( elapsed , ref timestamp ) ;
2014-01-07 08:52:02 +01:00
}
2014-01-06 01:57:54 +01:00
}
2014-01-13 11:22:33 +01:00
void RaiseUpdateFrame ( double elapsed , ref double timestamp )
2014-01-06 01:57:54 +01:00
{
2014-01-13 11:22:33 +01:00
// Raise UpdateFrame event
update_args . Time = elapsed ;
OnUpdateFrameInternal ( update_args ) ;
// Update UpdatePeriod/UpdateFrequency properties
update_period = elapsed ;
// Update UpdateTime property
update_timestamp = timestamp ;
timestamp = watch . Elapsed . TotalSeconds ;
update_time = timestamp - update_timestamp ;
2009-10-07 15:08:13 +02:00
}
2014-01-07 08:52:02 +01:00
2014-01-13 11:22:33 +01:00
void RaiseRenderFrame ( double elapsed , ref double timestamp )
2009-10-07 15:08:13 +02:00
{
2014-01-13 11:22:33 +01:00
// Raise RenderFrame event
render_args . Time = elapsed ;
OnRenderFrameInternal ( render_args ) ;
// Update RenderPeriod/UpdateFrequency properties
render_period = elapsed ;
// Update RenderTime property
render_timestamp = timestamp ;
timestamp = watch . Elapsed . TotalSeconds ;
render_time = timestamp - render_timestamp ;
2009-02-22 11:43:35 +01:00
}
#endregion
2009-08-21 00:22:38 +02:00
#region SwapBuffers
2009-02-22 11:43:35 +01:00
/// <summary>
2009-08-21 00:22:38 +02:00
/// Swaps the front and back buffer, presenting the rendered scene to the user.
2009-02-22 11:43:35 +01:00
/// </summary>
2009-08-21 00:22:38 +02:00
public void SwapBuffers ( )
2009-02-22 11:43:35 +01:00
{
2009-08-21 00:22:38 +02:00
EnsureUndisposed ( ) ;
this . Context . SwapBuffers ( ) ;
2009-02-22 11:43:35 +01:00
}
#endregion
2009-08-21 00:22:38 +02:00
#endregion
#region Properties
#region Context
2009-02-22 11:43:35 +01:00
/// <summary>
2009-08-21 00:22:38 +02:00
/// Returns the opengl IGraphicsContext associated with the current GameWindow.
2009-02-22 11:43:35 +01:00
/// </summary>
2009-08-21 00:22:38 +02:00
public IGraphicsContext Context
2009-02-22 11:43:35 +01:00
{
2009-08-21 00:22:38 +02:00
get
2009-02-22 11:43:35 +01:00
{
2009-08-21 00:22:38 +02:00
EnsureUndisposed ( ) ;
return glContext ;
2009-02-22 11:43:35 +01:00
}
}
#endregion
2009-08-21 00:22:38 +02:00
#region IsExiting
2009-02-22 11:43:35 +01:00
/// <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 17:49:39 +02:00
get
{
2009-08-21 00:22:38 +02:00
EnsureUndisposed ( ) ;
2009-06-02 17:49:39 +02:00
return isExiting ;
}
2009-02-22 11:43:35 +01:00
}
#endregion
2009-08-21 00:22:38 +02:00
#region Joysticks
2009-03-01 01:28:31 +01:00
/// <summary>
/// Gets a readonly IList containing all available OpenTK.Input.JoystickDevices.
/// </summary>
2014-01-14 23:53:24 +01:00
[Obsolete("Use OpenTK.Input.Joystick and GamePad instead")]
2009-03-01 01:28:31 +01:00
public IList < JoystickDevice > Joysticks
{
2014-01-14 23:53:24 +01:00
get { return LegacyJoystick . Joysticks ; }
2009-03-01 01:28:31 +01:00
}
#endregion
2009-08-21 00:22:38 +02:00
#region Keyboard
2009-02-22 11:43:35 +01:00
/// <summary>
2009-08-21 00:22:38 +02:00
/// Gets the primary Keyboard device, or null if no Keyboard exists.
2009-06-02 17:49:39 +02:00
/// </summary>
2009-08-21 00:22:38 +02:00
public KeyboardDevice Keyboard
2009-06-02 17:49:39 +02:00
{
2009-08-21 00:22:38 +02:00
get { return InputDriver . Keyboard . Count > 0 ? InputDriver . Keyboard [ 0 ] : null ; }
2009-06-02 17:49:39 +02:00
}
#endregion
2009-08-21 00:22:38 +02:00
#region Mouse
2009-06-02 17:49:39 +02:00
/// <summary>
2009-08-21 00:22:38 +02:00
/// Gets the primary Mouse device, or null if no Mouse exists.
2009-06-02 17:49:39 +02:00
/// </summary>
2009-08-21 00:22:38 +02:00
public MouseDevice Mouse
2009-06-02 17:49:39 +02:00
{
2009-08-21 00:22:38 +02:00
get { return InputDriver . Mouse . Count > 0 ? InputDriver . Mouse [ 0 ] : null ; }
2009-06-02 17:49:39 +02:00
}
#endregion
2009-08-21 00:22:38 +02:00
#region - - - GameWindow Timing - - -
2009-06-02 17:49:39 +02:00
2009-08-21 00:22:38 +02:00
// TODO: Disabled because it is not reliable enough. Use vsync as a workaround.
2009-06-02 17:49:39 +02:00
2009-08-21 00:22:38 +02:00
//#region public bool AllowSleep
2009-06-02 17:49:39 +02:00
2009-08-21 00:22:38 +02:00
//public bool AllowSleep
//{
// get { return allow_sleep; }
// set { allow_sleep = value; }
//}
2009-06-02 17:49:39 +02:00
2009-08-21 00:22:38 +02:00
//#endregion
2009-06-02 17:49:39 +02:00
2009-08-21 00:22:38 +02:00
#region RenderFrequency
2009-06-02 17:49:39 +02:00
/// <summary>
2009-11-06 17:46:28 +01:00
/// Gets a double representing the actual frequency of RenderFrame events, in hertz (i.e. fps or frames per second).
2009-06-02 17:49:39 +02:00
/// </summary>
2009-08-21 00:22:38 +02:00
public double RenderFrequency
2009-06-02 17:49:39 +02:00
{
get
{
2009-08-21 00:22:38 +02:00
EnsureUndisposed ( ) ;
if ( render_period = = 0.0 )
return 1.0 ;
return 1.0 / render_period ;
2009-06-02 17:49:39 +02:00
}
}
#endregion
2009-08-21 00:22:38 +02:00
#region RenderPeriod
2009-06-02 17:49:39 +02:00
2009-06-28 12:49:10 +02:00
/// <summary>
2009-08-21 00:22:38 +02:00
/// Gets a double representing the period of RenderFrame events, in seconds.
2009-06-28 12:49:10 +02:00
/// </summary>
2009-08-21 00:22:38 +02:00
public double RenderPeriod
2009-06-02 17:49:39 +02:00
{
get
{
2009-08-21 00:22:38 +02:00
EnsureUndisposed ( ) ;
return render_period ;
2009-06-02 17:49:39 +02:00
}
}
#endregion
2009-08-21 00:22:38 +02:00
#region RenderTime
2009-06-02 17:49:39 +02:00
/// <summary>
2009-08-21 00:22:38 +02:00
/// Gets a double representing the time spent in the RenderFrame function, in seconds.
2009-02-22 11:43:35 +01:00
/// </summary>
2009-08-21 00:22:38 +02:00
public double RenderTime
2009-02-22 11:43:35 +01:00
{
get
{
2009-08-21 00:22:38 +02:00
EnsureUndisposed ( ) ;
return render_time ;
2009-02-22 11:43:35 +01:00
}
2009-08-21 00:22:38 +02:00
protected set
2009-02-22 11:43:35 +01:00
{
2009-08-21 00:22:38 +02:00
EnsureUndisposed ( ) ;
render_time = value ;
2009-02-22 11:43:35 +01:00
}
}
#endregion
2009-08-21 00:22:38 +02:00
#region TargetRenderFrequency
2009-02-22 11:43:35 +01:00
/// <summary>
2009-11-06 17:46:28 +01:00
/// Gets or sets a double representing the target render frequency, in hertz.
2009-02-22 11:43:35 +01: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>
2014-01-14 14:20:38 +01:00
/// <para>Values lower than 1.0Hz are clamped to 0.0. Values higher than 500.0Hz are clamped to 200.0Hz.</para>
2009-02-22 11:43:35 +01:00
/// </remarks>
public double TargetRenderFrequency
{
get
{
2009-08-21 00:22:38 +02:00
EnsureUndisposed ( ) ;
2009-02-22 11:43:35 +01:00
if ( TargetRenderPeriod = = 0.0 )
return 0.0 ;
return 1.0 / TargetRenderPeriod ;
}
set
{
2009-08-21 00:22:38 +02:00
EnsureUndisposed ( ) ;
2009-02-22 11:43:35 +01:00
if ( value < 1.0 )
{
TargetRenderPeriod = 0.0 ;
}
2014-01-14 14:20:38 +01:00
else if ( value < = MaxFrequency )
2009-02-22 11:43:35 +01:00
{
TargetRenderPeriod = 1.0 / value ;
}
2014-01-14 14:20:38 +01:00
else Debug . Print ( "Target render frequency clamped to {0}Hz." , MaxFrequency ) ;
2009-02-22 11:43:35 +01:00
}
}
#endregion
2009-08-21 00:22:38 +02:00
#region TargetRenderPeriod
2009-02-22 11:43:35 +01:00
/// <summary>
2009-08-21 00:22:38 +02:00
/// Gets or sets a double representing the target render period, in seconds.
2009-02-22 11:43:35 +01:00
/// </summary>
/// <remarks>
2009-08-21 00:22:38 +02: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>
2014-01-14 14:20:38 +01:00
/// <para>Values lower than 0.002 seconds (500Hz) are clamped to 0.0. Values higher than 1.0 seconds (1Hz) are clamped to 1.0.</para>
2009-02-22 11:43:35 +01:00
/// </remarks>
2009-08-21 00:22:38 +02:00
public double TargetRenderPeriod
2009-02-22 11:43:35 +01:00
{
get
{
2009-08-21 00:22:38 +02:00
EnsureUndisposed ( ) ;
return target_render_period ;
2009-02-22 11:43:35 +01:00
}
set
{
2009-08-21 00:22:38 +02:00
EnsureUndisposed ( ) ;
2014-01-14 14:20:38 +01:00
if ( value < = 1 / MaxFrequency )
2009-02-22 11:43:35 +01:00
{
2009-08-21 00:22:38 +02:00
target_render_period = 0.0 ;
2009-02-22 11:43:35 +01:00
}
else if ( value < = 1.0 )
{
2009-08-21 00:22:38 +02:00
target_render_period = value ;
2009-02-22 11:43:35 +01:00
}
2009-08-21 00:22:38 +02:00
else Debug . Print ( "Target render period clamped to 1.0 seconds." ) ;
2009-02-22 11:43:35 +01:00
}
}
#endregion
2009-08-21 00:22:38 +02:00
#region TargetUpdateFrequency
2009-02-22 11:43:35 +01:00
/// <summary>
2009-11-06 17:46:28 +01:00
/// Gets or sets a double representing the target update frequency, in hertz.
2009-02-22 11:43:35 +01: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>
2014-01-14 14:20:38 +01:00
/// <para>Values lower than 1.0Hz are clamped to 0.0. Values higher than 500.0Hz are clamped to 500.0Hz.</para>
2009-02-22 11:43:35 +01:00
/// </remarks>
public double TargetUpdateFrequency
{
get
{
2009-08-21 00:22:38 +02:00
EnsureUndisposed ( ) ;
2009-02-22 11:43:35 +01:00
if ( TargetUpdatePeriod = = 0.0 )
return 0.0 ;
return 1.0 / TargetUpdatePeriod ;
}
set
{
2009-08-21 00:22:38 +02:00
EnsureUndisposed ( ) ;
2009-02-22 11:43:35 +01:00
if ( value < 1.0 )
{
TargetUpdatePeriod = 0.0 ;
}
2014-01-14 14:20:38 +01:00
else if ( value < = MaxFrequency )
2009-02-22 11:43:35 +01:00
{
TargetUpdatePeriod = 1.0 / value ;
}
2014-01-14 14:20:38 +01:00
else Debug . Print ( "Target render frequency clamped to {0}Hz." , MaxFrequency ) ;
2009-02-22 11:43:35 +01:00
}
}
#endregion
2009-08-21 00:22:38 +02:00
#region TargetUpdatePeriod
2009-02-22 11:43:35 +01:00
/// <summary>
2009-08-21 00:22:38 +02:00
/// Gets or sets a double representing the target update period, in seconds.
2009-02-22 11:43:35 +01:00
/// </summary>
2009-08-21 00:22:38 +02:00
/// <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>
2014-01-14 14:20:38 +01:00
/// <para>Values lower than 0.002 seconds (500Hz) are clamped to 0.0. Values higher than 1.0 seconds (1Hz) are clamped to 1.0.</para>
2009-08-21 00:22:38 +02:00
/// </remarks>
public double TargetUpdatePeriod
2009-02-22 11:43:35 +01:00
{
get
{
2009-08-21 00:22:38 +02:00
EnsureUndisposed ( ) ;
return target_update_period ;
2009-02-22 11:43:35 +01:00
}
2009-08-21 00:22:38 +02:00
set
2009-02-22 11:43:35 +01:00
{
2009-08-21 00:22:38 +02:00
EnsureUndisposed ( ) ;
2014-01-14 14:20:38 +01:00
if ( value < = 1 / MaxFrequency )
2009-08-21 00:22:38 +02:00
{
target_update_period = 0.0 ;
}
else if ( value < = 1.0 )
{
target_update_period = value ;
}
2014-01-14 14:20:38 +01:00
else Debug . Print ( "Target update period clamped to 1.0 seconds." ) ;
2009-02-22 11:43:35 +01:00
}
}
#endregion
2009-08-21 00:22:38 +02:00
#region UpdateFrequency
2009-02-22 11:43:35 +01:00
/// <summary>
2009-11-06 17:46:28 +01:00
/// Gets a double representing the frequency of UpdateFrame events, in hertz.
2009-02-22 11:43:35 +01:00
/// </summary>
public double UpdateFrequency
{
get
{
2009-08-21 00:22:38 +02:00
EnsureUndisposed ( ) ;
2009-02-22 11:43:35 +01:00
if ( update_period = = 0.0 )
return 1.0 ;
return 1.0 / update_period ;
}
}
#endregion
2009-08-21 00:22:38 +02:00
#region UpdatePeriod
2009-02-22 11:43:35 +01:00
/// <summary>
/// Gets a double representing the period of UpdateFrame events, in seconds.
/// </summary>
public double UpdatePeriod
{
get
{
2009-08-21 00:22:38 +02:00
EnsureUndisposed ( ) ;
2009-02-22 11:43:35 +01:00
return update_period ;
}
}
#endregion
2009-08-21 00:22:38 +02:00
#region UpdateTime
2009-02-22 11:43:35 +01:00
/// <summary>
2009-08-21 00:22:38 +02:00
/// Gets a double representing the time spent in the UpdateFrame function, in seconds.
2009-02-22 11:43:35 +01:00
/// </summary>
2009-08-21 00:22:38 +02:00
public double UpdateTime
2009-02-22 11:43:35 +01:00
{
2009-08-21 00:22:38 +02:00
get
{
EnsureUndisposed ( ) ;
return update_time ;
}
2009-02-22 11:43:35 +01:00
}
#endregion
2009-08-21 00:22:38 +02:00
#endregion
#region VSync
2009-02-22 11:43:35 +01:00
/// <summary>
2009-08-21 00:22:38 +02:00
/// Gets or sets the VSyncMode.
2009-02-22 11:43:35 +01:00
/// </summary>
2009-08-21 00:22:38 +02:00
public VSyncMode VSync
2009-02-22 11:43:35 +01:00
{
2009-08-21 00:22:38 +02:00
get
{
EnsureUndisposed ( ) ;
GraphicsContext . Assert ( ) ;
return vsync ;
}
set
{
EnsureUndisposed ( ) ;
GraphicsContext . Assert ( ) ;
2013-09-27 14:41:37 +02:00
switch ( value )
{
case VSyncMode . On :
Context . SwapInterval = 1 ;
break ;
case VSyncMode . Off :
Context . SwapInterval = 0 ;
break ;
case VSyncMode . Adaptive :
Context . SwapInterval = - 1 ;
break ;
}
vsync = value ;
2009-08-21 00:22:38 +02:00
}
2009-02-22 11:43:35 +01:00
}
#endregion
2010-03-11 23:53:11 +01:00
#region WindowState
/// <summary>
/// Gets or states the state of the NativeWindow.
/// </summary>
public override WindowState WindowState
{
get
{
return base . WindowState ;
}
set
{
base . WindowState = value ;
Debug . Print ( "Updating Context after setting WindowState to {0}" , value ) ;
if ( Context ! = null )
Context . Update ( WindowInfo ) ;
}
}
#endregion
#endregion
#region Events
/// <summary>
2009-08-21 00:22:38 +02:00
/// Occurs before the window is displayed for the first time.
/// </summary>
2010-10-28 11:37:57 +02:00
public event EventHandler < EventArgs > Load = delegate { } ;
2009-02-22 11:43:35 +01:00
/// <summary>
2009-08-21 00:22:38 +02:00
/// Occurs when it is time to render a frame.
/// </summary>
2010-10-28 11:37:57 +02:00
public event EventHandler < FrameEventArgs > RenderFrame = delegate { } ;
2009-08-21 00:22:38 +02:00
/// <summary>
/// Occurs before the window is destroyed.
/// </summary>
2010-10-28 11:37:57 +02:00
public event EventHandler < EventArgs > Unload = delegate { } ;
2009-08-21 00:22:38 +02:00
/// <summary>
/// Occurs when it is time to update a frame.
/// </summary>
2010-10-28 11:37:57 +02:00
public event EventHandler < FrameEventArgs > UpdateFrame = delegate { } ;
2009-08-21 00:22:38 +02:00
#endregion
#endregion
#region - - - Protected Members - - -
#region Dispose
/// <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 ) { }
#endregion
#region OnRenderFrame
/// <summary>
2009-09-04 23:27:05 +02:00
/// Called when the frame is rendered.
2009-08-21 00:22:38 +02:00
/// </summary>
/// <param name="e">Contains information necessary for frame rendering.</param>
/// <remarks>
2009-09-04 23:27:05 +02:00
/// Subscribe to the <see cref="RenderFrame"/> event instead of overriding this method.
2009-08-21 00:22:38 +02:00
/// </remarks>
2009-09-04 23:27:05 +02:00
protected virtual void OnRenderFrame ( FrameEventArgs e )
{
2010-10-28 11:37:57 +02:00
RenderFrame ( this , e ) ;
2009-09-04 23:27:05 +02:00
}
2009-08-21 00:22:38 +02:00
#endregion
#region OnUpdateFrame
/// <summary>
2009-09-04 23:27:05 +02:00
/// Called when the frame is updated.
2009-08-21 00:22:38 +02:00
/// </summary>
/// <param name="e">Contains information necessary for frame updating.</param>
/// <remarks>
2009-09-04 23:27:05 +02:00
/// Subscribe to the <see cref="UpdateFrame"/> event instead of overriding this method.
2009-08-21 00:22:38 +02:00
/// </remarks>
2009-09-04 23:27:05 +02:00
protected virtual void OnUpdateFrame ( FrameEventArgs e )
{
2010-10-28 11:37:57 +02:00
UpdateFrame ( this , e ) ;
2009-09-04 23:27:05 +02:00
}
2009-08-21 00:22:38 +02:00
#endregion
#region OnWindowInfoChanged
/// <summary>
/// Called when the WindowInfo for this GameWindow has changed.
2009-02-22 11:43:35 +01:00
/// </summary>
2009-08-21 00:22:38 +02:00
/// <param name="e">Not used.</param>
protected virtual void OnWindowInfoChanged ( EventArgs e ) { }
#endregion
2010-03-11 23:53:11 +01:00
#region OnResize
2009-11-09 07:51:52 +01:00
2010-10-28 11:00:07 +02:00
/// <summary>
/// Called when this window is resized.
/// </summary>
/// <param name="e">Not used.</param>
/// <remarks>
/// You will typically wish to update your viewport whenever
/// the window is resized. See the
2010-10-28 11:31:20 +02:00
/// <see cref="OpenTK.Graphics.OpenGL.GL.Viewport(int, int, int, int)"/> method.
2010-10-28 11:00:07 +02:00
/// </remarks>
2010-03-11 23:53:11 +01:00
protected override void OnResize ( EventArgs e )
{
base . OnResize ( e ) ;
glContext . Update ( base . WindowInfo ) ;
}
2009-11-09 07:51:52 +01:00
2010-03-11 23:53:11 +01:00
#endregion
2009-11-09 07:51:52 +01:00
2010-03-11 23:53:11 +01:00
#endregion
2009-08-21 00:22:38 +02:00
2010-03-11 23:53:11 +01:00
#region - - - Private Members - - -
2009-08-21 00:22:38 +02:00
2010-03-11 23:53:11 +01:00
#region OnLoadInternal
2009-08-21 00:22:38 +02:00
2010-03-11 23:53:11 +01:00
private void OnLoadInternal ( EventArgs e )
2009-08-21 00:22:38 +02:00
{
OnLoad ( e ) ;
2009-02-22 11:43:35 +01:00
}
2009-08-21 00:22:38 +02:00
#endregion
#region OnRenderFrameInternal
2009-09-04 23:27:05 +02:00
private void OnRenderFrameInternal ( FrameEventArgs e ) { if ( Exists & & ! isExiting ) OnRenderFrame ( e ) ; }
2009-08-21 00:22:38 +02:00
#endregion
#region OnUnloadInternal
2009-09-04 23:27:05 +02:00
private void OnUnloadInternal ( EventArgs e ) { OnUnload ( e ) ; }
2009-08-21 00:22:38 +02:00
#endregion
#region OnUpdateFrameInternal
2009-09-04 23:27:05 +02:00
private void OnUpdateFrameInternal ( FrameEventArgs e ) { if ( Exists & & ! isExiting ) OnUpdateFrame ( e ) ; }
2009-08-21 00:22:38 +02:00
#endregion
#region OnWindowInfoChangedInternal
private void OnWindowInfoChangedInternal ( EventArgs e )
2009-02-22 11:43:35 +01:00
{
2009-08-21 00:22:38 +02:00
glContext . MakeCurrent ( WindowInfo ) ;
OnWindowInfoChanged ( e ) ;
2009-02-22 11:43:35 +01:00
}
2009-08-21 00:22:38 +02:00
#endregion
#endregion
2009-02-22 11:43:35 +01:00
}
#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>
2010-02-03 20:04:42 +01:00
/// VSync enabled, unless framerate falls below one half of target framerate.
/// If no target framerate is specified, this behaves exactly like <see cref="VSyncMode.On"/>.
2009-02-22 11:43:35 +01:00
/// </summary>
Adaptive ,
}
#endregion
2014-01-07 22:09:02 +01:00
}