2007-07-23 02:15:18 +02:00
#region - - - License - - -
/ * Copyright ( c ) 2006 , 2007 Stefanos Apostolopoulos
* See license . txt for license info
* /
#endregion
using System ;
using System.Collections.Generic ;
using System.Text ;
2007-07-27 00:56:55 +02:00
using System.Diagnostics ;
2007-07-23 02:15:18 +02:00
using OpenTK.Platform ;
2007-08-06 11:22:04 +02:00
using OpenTK.Input ;
using System.Threading ;
2007-09-03 00:52:00 +02:00
using OpenTK.OpenGL ;
2007-07-23 02:15:18 +02:00
namespace OpenTK
{
2007-08-04 14:09:58 +02:00
public class GameWindow : OpenTK . Platform . IGameWindow
2007-07-23 02:15:18 +02:00
{
2007-08-05 15:42:31 +02:00
#region - - - Fields - - -
private INativeGLWindow glWindow ;
2007-07-23 02:15:18 +02:00
private ResizeEventArgs resizeEventArgs = new ResizeEventArgs ( ) ;
2007-08-04 14:09:58 +02:00
private DisplayMode mode ;
2007-07-23 02:15:18 +02:00
2007-08-05 15:42:31 +02:00
private InputDriver driver ;
2007-08-06 11:22:04 +02:00
private bool isExiting ;
private bool disposed ;
2007-08-05 15:42:31 +02:00
#endregion
2007-08-03 02:14:31 +02:00
2007-07-23 02:15:18 +02:00
#region - - - Contructors - - -
/// <summary>
/// Constructs a new GameWindow, using a safe DisplayMode.
/// </summary>
public GameWindow ( )
{
if ( Environment . OSVersion . Platform = = PlatformID . Win32NT | |
Environment . OSVersion . Platform = = PlatformID . Win32Windows )
{
glWindow = new OpenTK . Platform . Windows . WinGLNative ( ) ;
}
2007-08-05 15:42:31 +02:00
else if ( Environment . OSVersion . Platform = = PlatformID . Unix )
2007-07-23 02:15:18 +02:00
{
glWindow = new OpenTK . Platform . X11 . X11GLNative ( ) ;
}
else
{
throw new PlatformNotSupportedException (
2007-08-05 15:42:31 +02:00
"Your platform is not currently supported. Refer to http://opentk.sourceforge.net for more information."
2007-07-23 02:15:18 +02:00
) ;
}
glWindow . Resize + = new ResizeEvent ( glWindow_Resize ) ;
2007-08-05 19:54:11 +02:00
glWindow . Create + = new CreateEvent ( glWindow_CreateInputDriver ) ;
2007-08-10 18:55:24 +02:00
glWindow . Destroy + = new DestroyEvent ( glWindow_Destroy ) ;
2007-08-06 13:22:18 +02:00
}
void glWindow_Destroy ( object sender , EventArgs e )
{
2007-08-10 18:55:24 +02:00
Debug . Print ( "GameWindow destruction imminent." ) ;
2007-08-06 13:22:18 +02:00
this . isExiting = true ;
2007-08-10 18:55:24 +02:00
this . OnDestroy ( EventArgs . Empty ) ;
2007-08-06 14:13:50 +02:00
glWindow . Destroy - = glWindow_Destroy ;
2007-09-02 02:13:52 +02:00
//this.Dispose();
2007-07-23 02:15:18 +02:00
}
2007-08-05 19:54:11 +02:00
void glWindow_CreateInputDriver ( object sender , EventArgs e )
2007-07-23 02:15:18 +02:00
{
2007-08-04 14:09:58 +02:00
//glWindow.Context.MakeCurrent();
2007-08-05 19:54:11 +02:00
2007-08-06 01:09:05 +02:00
if ( driver = = null )
driver = new InputDriver ( this . WindowInfo ) ;
2007-08-05 19:54:11 +02:00
glWindow . Create - = glWindow_CreateInputDriver ;
2007-08-04 14:09:58 +02:00
2007-07-23 02:15:18 +02:00
this . OnCreate ( e ) ;
}
void glWindow_Resize ( object sender , ResizeEventArgs e )
{
this . OnResize ( e ) ;
}
#endregion
2007-08-06 11:22:04 +02:00
#region - - - Internal Properties - - -
2007-08-05 15:42:31 +02:00
#region public IList < OpenTK . Input . IInputDevice > Input
2007-08-06 01:09:05 +02:00
internal InputDriver InputDriver
2007-08-05 15:42:31 +02:00
{
get
{
2007-08-10 18:55:24 +02:00
if ( driver = = null & & ! this . IsExiting )
2007-08-05 15:42:31 +02:00
{
2007-08-06 01:09:05 +02:00
Debug . WriteLine ( "WARNING: Accessed null InputDriver - creating new. This may indicate a prorgamming error." ) ;
2007-08-05 15:42:31 +02:00
driver = new InputDriver ( this . WindowInfo ) ;
}
2007-08-06 01:09:05 +02:00
return driver ;
2007-08-05 15:42:31 +02:00
}
}
#endregion
2007-08-06 11:22:04 +02:00
#endregion
2007-08-05 15:42:31 +02:00
#region - - - INativeGLWindow Members - - -
2007-08-04 14:09:58 +02:00
#region public void Exit ( )
/// <summary>
/// Gracefully exits the current GameWindow.
2007-08-06 11:22:04 +02:00
/// Override if you want to provide yor own exit sequence.
/// If you override this method, place a call to base.Exit(), to ensure
/// proper OpenTK shutdown.
2007-08-04 14:09:58 +02:00
/// </summary>
2007-08-06 11:22:04 +02:00
public virtual void Exit ( )
2007-08-04 14:09:58 +02:00
{
2007-08-06 11:22:04 +02:00
isExiting = true ;
//glWindow.Exit();
//this.Dispose();
2007-08-04 14:09:58 +02:00
}
#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
{
get { return glWindow . IsIdle ; }
}
#endregion
#region public bool Fullscreen
public bool Fullscreen
{
get { return glWindow . Fullscreen ; }
set { glWindow . Fullscreen = value ; }
}
#endregion
2007-08-04 14:09:58 +02:00
#region public IGLContext Context
2007-07-23 02:15:18 +02:00
2007-08-04 14:09:58 +02:00
/// <summary>
/// Returns the opengl IGLontext associated with the current GameWindow.
/// Forces window creation.
/// </summary>
public IGLContext Context
2007-07-23 02:15:18 +02:00
{
2007-08-04 14:09:58 +02:00
get
{
2007-08-10 18:55:24 +02:00
if ( ! this . Exists & & ! this . IsExiting )
{
Debug . WriteLine ( "WARNING: OpenGL Context accessed before creating a render window. This may indicate a programming error. Force-creating a render window." ) ;
mode = new DisplayMode ( 640 , 480 ) ;
this . CreateWindow ( mode ) ;
}
return glWindow . Context ;
}
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-08-06 11:22:04 +02:00
/// Gets a value indicating whether a render window has been 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-08-05 15:42:31 +02:00
#region public IWindowInfo WindowInfo
public IWindowInfo WindowInfo
{
get { return glWindow . WindowInfo ; }
}
#endregion
2007-08-06 13:22:18 +02:00
#region public void CreateWindow ( DisplayMode mode )
/// <summary>
2007-08-10 18:55:24 +02:00
/// Creates a new render window. The Create event is raised after the window creation
/// is complete, to allow resource initalisation.
2007-08-06 13:22:18 +02:00
/// </summary>
/// <param name="mode">The DisplayMode of the render window.</param>
/// <exception cref="ApplicationException">Occurs when a render window already exists.</exception>
public void CreateWindow ( DisplayMode mode )
{
if ( ! Exists )
{
2007-08-20 15:47:14 +02:00
try
{
glWindow . CreateWindow ( mode ) ;
}
catch ( ApplicationException expt )
{
Debug . Print ( expt . ToString ( ) ) ;
throw ;
}
2007-08-21 14:21:01 +02:00
//OpenTK.OpenGL.GL.LoadAll();
2007-08-06 13:22:18 +02:00
}
else
{
throw new ApplicationException ( "A render window already exists" ) ;
}
}
#endregion
#region OnCreate
2007-09-02 02:13:52 +02:00
[Obsolete("The Create event is obsolete and will be removed on later versions. Use the Load event instead.")]
2007-08-06 13:22:18 +02:00
public event CreateEvent Create ;
2007-08-10 18:55:24 +02:00
/// <summary>
/// Raises the Create event. Override in derived classes to initialize resources.
/// </summary>
/// <param name="e"></param>
2007-09-02 02:13:52 +02:00
[Obsolete("The OnCreate method is obsolete and will be removed on later versions. Use the OnLoad method instead.")]
2007-08-06 13:22:18 +02:00
public virtual void OnCreate ( EventArgs e )
{
2007-08-20 12:46:37 +02:00
Debug . WriteLine ( "Firing GameWindow.Create event" ) ;
2007-08-06 13:22:18 +02:00
if ( this . Create ! = null )
{
this . Create ( this , e ) ;
}
}
#endregion
#region public void DestroyWindow ( )
2007-08-10 18:55:24 +02:00
/// <summary>
/// Destroys the GameWindow. The Destroy event is raised before destruction commences
/// (while the opengl context still exists), to allow resource cleanup.
/// </summary>
2007-08-06 13:22:18 +02:00
public void DestroyWindow ( )
{
if ( Exists )
{
glWindow . DestroyWindow ( ) ;
}
else
{
throw new ApplicationException ( "Tried to destroy inexistent window." ) ;
}
}
#endregion
#region OnDestroy
2007-08-10 18:55:24 +02:00
/// <summary>
/// Raises the Destroy event. Override in derived classes, to modify the shutdown
/// sequence (e.g. to release resources before shutdown).
/// </summary>
/// <param name="e"></param>
2007-08-06 13:22:18 +02:00
public virtual void OnDestroy ( EventArgs e )
{
2007-08-20 12:46:37 +02:00
Debug . WriteLine ( "Firing GameWindow.Destroy event" ) ;
2007-08-06 13:22:18 +02:00
if ( this . Destroy ! = null )
{
this . Destroy ( this , e ) ;
}
}
public event DestroyEvent Destroy ;
#endregion
2007-07-23 02:15:18 +02:00
#endregion
#region - - - IGameWindow Members - - -
2007-08-04 14:09:58 +02:00
#region public virtual void Run ( )
2007-07-23 02:15:18 +02:00
/// <summary>
2007-08-04 14:09:58 +02:00
/// Runs the default game loop on GameWindow (process event->update frame->render frame).
2007-07-23 02:15:18 +02:00
/// </summary>
/// <remarks>
/// <para>
/// A default game loop consists of three parts: Event processing,
/// a frame update and a frame render.
/// </para>
/// <para>
/// Override this function if you want to change the behaviour of the
/// default game loop. If you override this function, you must place
2007-08-04 14:09:58 +02:00
/// a call to the ProcessEvents function, to ensure window will respond
2007-07-23 02:15:18 +02:00
/// to Operating System events.
/// </para>
/// </remarks>
public virtual void Run ( )
{
2007-08-06 13:22:18 +02:00
this . OnLoad ( EventArgs . Empty ) ;
resizeEventArgs . Width = this . Width ;
resizeEventArgs . Height = this . Height ;
this . OnResize ( resizeEventArgs ) ;
Debug . Print ( "Entering main loop" ) ;
2007-08-10 18:55:24 +02:00
while ( this . Exists & & ! IsExiting )
2007-07-23 02:15:18 +02:00
{
this . ProcessEvents ( ) ;
2007-08-06 13:22:18 +02:00
if ( ! IsExiting )
{
this . OnUpdateFrame ( EventArgs . Empty ) ;
this . OnRenderFrame ( EventArgs . Empty ) ;
}
2007-07-23 02:15:18 +02:00
}
2007-08-06 11:22:04 +02:00
2007-08-10 18:55:24 +02:00
if ( this . Exists )
2007-08-06 11:22:04 +02:00
{
2007-08-10 18:55:24 +02:00
glWindow . DestroyWindow ( ) ;
while ( this . Exists )
2007-08-06 13:22:18 +02:00
{
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 ( )
{
2007-08-06 01:09:05 +02:00
if ( driver ! = null )
driver . ProcessEvents ( ) ;
2007-07-23 02:15:18 +02:00
glWindow . ProcessEvents ( ) ;
}
2007-08-04 14:09:58 +02:00
#endregion
2007-08-06 13:22:18 +02:00
#region public virtual void OnRenderFrame ( EventArgs e )
2007-08-04 14:09:58 +02:00
/// <summary>
/// Raises the RenderFrame event. Override in derived classes to render a frame.
/// </summary>
/// <remarks>
/// If overriden, the base.OnRenderFrame() function should be called, to ensure
/// listeners are notified of RenderFrame events.
/// </remarks>
2007-08-06 13:22:18 +02:00
public virtual void OnRenderFrame ( EventArgs 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
{
Debug . Print ( "WARNING: RenderFrame event raised, without a valid render window. This may indicate a programming error. Creating render window." ) ;
mode = new DisplayMode ( 640 , 480 ) ;
this . CreateWindow ( mode ) ;
}
if ( RenderFrame ! = null )
2007-08-06 13:22:18 +02:00
RenderFrame ( this , e ) ;
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-08-06 13:22:18 +02:00
#region public virtual void OnUpdateFrame ( EventArgs e )
2007-08-04 14:09:58 +02:00
/// <summary>
/// Raises the UpdateFrame event. Override in derived classes to update a frame.
/// </summary>
/// <remarks>
/// If overriden, the base.OnUpdateFrame() function should be called, to ensure
/// listeners are notified of UpdateFrame events.
/// </remarks>
2007-08-06 13:22:18 +02:00
public virtual void OnUpdateFrame ( EventArgs 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
{
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 ) ;
}
if ( UpdateFrame ! = null )
2007-08-06 13:22:18 +02:00
UpdateFrame ( this , e ) ;
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
#region public virtual void OnLoad ( EventArgs e )
2007-08-04 14:09:58 +02:00
/// <summary>
2007-08-06 13:22:18 +02:00
/// Raises the Load event. Override to load resources that should
/// be maintained for the lifetime of the application.
2007-08-04 14:09:58 +02:00
/// </summary>
2007-08-06 13:22:18 +02:00
public virtual void OnLoad ( EventArgs e )
{
Debug . Print ( "Firing GameWindow.Load event." ) ;
if ( this . Load ! = null )
{
this . Load ( this , e ) ;
}
}
/// <summary>
/// Occurs after the GameWindow has been created, but before
/// entering the main loop.
/// </summary>
public event LoadEvent Load ;
#endregion
2007-07-23 02:15:18 +02:00
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
/// OpenTK.OpenGL functions or properties.
/// </summary>
public bool IsExiting
{
get { return isExiting ; }
}
#endregion
#region public IList < Keyboard > Keyboard
/// <summary>
/// Gets the list of available Keyboard devices.
/// </summary>
public IList < Keyboard > Keyboard
{
get
{
return InputDriver . Keyboard ;
}
}
#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
{
get { return glWindow . Width ; }
set
{
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
{
get { return glWindow . Height ; }
set
{
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 ;
public event ResizeEvent Resize ;
/// <summary>
/// Raises the Resize event.
/// </summary>
/// <param name="e">Contains the new Width and Height of the window.</param>
protected virtual void OnResize ( ResizeEventArgs e )
{
2007-08-06 13:22:18 +02:00
Debug . Print ( "Firing GameWindow.Resize event: {0}." , e . ToString ( ) ) ;
2007-07-23 02:15:18 +02:00
if ( this . Resize ! = null )
this . Resize ( this , e ) ;
}
#endregion
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
#region - - - IDisposable Members - - -
public void Dispose ( )
{
2007-08-06 11:22:04 +02:00
Dispose ( true ) ;
}
private void Dispose ( bool manual )
{
if ( ! disposed )
{
// Is this safe? Maybe 'Debug' has been disposed, too...
2007-09-02 02:13:52 +02:00
//Debug.Print("{0} disposing GameWindow.", manual ? "Manually" : "Automatically");
2007-08-06 11:22:04 +02:00
if ( manual )
{
2007-09-02 02:13:52 +02:00
if ( driver ! = null )
{
driver . Dispose ( ) ;
driver = null ;
}
2007-08-06 11:22:04 +02:00
if ( glWindow ! = null )
{
glWindow . Dispose ( ) ;
glWindow = null ;
}
}
disposed = true ;
}
}
2007-07-23 02:15:18 +02:00
#endregion
}
}