2009-09-03 21:01:11 +02:00
using System ;
using System.ComponentModel ;
using System.Drawing ;
using OpenTK.Graphics ;
using OpenTK.Input ;
using OpenTK.Platform ;
namespace OpenTK
{
/// <summary>
/// Instances of this class implement the <see cref="OpenTK.INativeWindow"/> interface on the current platform.
/// </summary>
public class NativeWindow : INativeWindow
{
#region - - - Fields - - -
2009-09-04 23:27:05 +02:00
private readonly GameWindowFlags options ;
private readonly DisplayDevice device ;
2009-09-03 21:01:11 +02:00
private readonly INativeWindow implementation ;
2009-09-04 23:27:05 +02:00
private bool disposed , events ;
2009-09-03 21:01:11 +02:00
#endregion
#region - - - Contructors - - -
#region NativeWindow ( )
/// <summary>Constructs a new NativeWindow with default attributes without enabling events.</summary>
public NativeWindow ( )
2009-09-04 23:27:05 +02:00
: this ( 640 , 480 , "OpenTK Native Window" , GameWindowFlags . Default , GraphicsMode . Default , DisplayDevice . Default ) { }
2009-09-03 21:01:11 +02:00
#endregion
// TODO: Remaining constructors.
#region NativeWindow ( int width , int height , string title , GameWindowFlags options , GraphicsMode mode , DisplayDevice device )
/// <summary>Constructs a new centered NativeWindow with the specified attributes.</summary>
/// <param name="width">The width of the NativeWindow in pixels.</param>
/// <param name="height">The height of the NativeWindow in pixels.</param>
/// <param name="title">The title of the NativeWindow.</param>
/// <param name="options">GameWindow options specifying window appearance and behavior.</param>
/// <param name="mode">The OpenTK.Graphics.GraphicsMode of the NativeWindow.</param>
/// <param name="device">The OpenTK.Graphics.DisplayDevice to construct the NativeWindow in.</param>
/// <exception cref="System.ArgumentOutOfRangeException">If width or height is less than 1.</exception>
/// <exception cref="System.ArgumentNullException">If mode or device is null.</exception>
2009-09-04 23:27:05 +02:00
public NativeWindow ( int width , int height , string title , GameWindowFlags options , GraphicsMode mode , DisplayDevice device )
2009-09-03 21:01:11 +02:00
: this ( device . Bounds . Left + ( device . Bounds . Width - width ) / 2 ,
device . Bounds . Top + ( device . Bounds . Height - height ) / 2 ,
2009-09-04 23:27:05 +02:00
width , height , title , options , mode , device ) { }
2009-09-03 21:01:11 +02:00
#endregion
2009-09-04 23:27:05 +02:00
#region NativeWindow ( int x , int y , int width , int height , string title , GameWindowFlags options , GraphicsMode mode , DisplayDevice device )
2009-09-03 21:01:11 +02:00
/// <summary>Constructs a new NativeWindow with the specified attributes.</summary>
/// <param name="x">Horizontal screen space coordinate of the NativeWindow's origin.</param>
/// <param name="y">Vertical screen space coordinate of the NativeWindow's origin.</param>
/// <param name="width">The width of the NativeWindow in pixels.</param>
/// <param name="height">The height of the NativeWindow in pixels.</param>
/// <param name="title">The title of the NativeWindow.</param>
/// <param name="options">GameWindow options specifying window appearance and behavior.</param>
/// <param name="mode">The OpenTK.Graphics.GraphicsMode of the NativeWindow.</param>
/// <param name="device">The OpenTK.Graphics.DisplayDevice to construct the NativeWindow in.</param>
/// <exception cref="System.ArgumentOutOfRangeException">If width or height is less than 1.</exception>
/// <exception cref="System.ArgumentNullException">If mode or device is null.</exception>
2009-09-04 23:27:05 +02:00
public NativeWindow ( int x , int y , int width , int height , string title , GameWindowFlags options , GraphicsMode mode , DisplayDevice device )
2009-09-03 21:01:11 +02:00
{
// TODO: Should a constraint be added for the position?
if ( width < 1 )
throw new ArgumentOutOfRangeException ( "width" , "Must be greater than zero." ) ;
if ( height < 1 )
throw new ArgumentOutOfRangeException ( "height" , "Must be greater than zero." ) ;
if ( mode = = null )
throw new ArgumentNullException ( "mode" ) ;
if ( device = = null )
throw new ArgumentNullException ( "device" ) ;
2009-09-04 23:27:05 +02:00
this . options = options ;
this . device = device ;
implementation = Factory . Default . CreateNativeWindow ( x , y , width , height , title , mode , options , this . device ) ;
2009-09-03 21:01:11 +02:00
if ( ( options & GameWindowFlags . Fullscreen ) ! = 0 )
{
2009-09-04 23:27:05 +02:00
this . device . ChangeResolution ( width , height , mode . ColorFormat . BitsPerPixel , 0 ) ;
2009-09-03 21:01:11 +02:00
WindowState = WindowState . Fullscreen ;
}
}
#endregion
#endregion
#region - - - INativeWindow Members - - -
#region Methods
#region Close
/// <summary>
/// Closes the NativeWindow.
/// </summary>
public void Close ( )
{
EnsureUndisposed ( ) ;
implementation . Close ( ) ;
}
#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 implementation . 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>
public System . Drawing . Point PointToScreen ( System . Drawing . Point point )
{
// 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 ) ;
point . X - = trans . X ;
point . Y - = trans . Y ;
return point ;
}
#endregion
#region ProcessEvents
/// <summary>
/// Processes operating system events until the NativeWindow becomes idle.
/// </summary>
public void ProcessEvents ( )
{
2009-09-04 23:27:05 +02:00
ProcessEvents ( false ) ;
2009-09-03 21:01:11 +02:00
}
#endregion
#endregion
#region Properties
#region Bounds
/// <summary>
/// Gets or sets a <see cref="System.Drawing.Rectangle"/> structure that 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
{
EnsureUndisposed ( ) ;
return implementation . Bounds ;
}
set
{
EnsureUndisposed ( ) ;
implementation . Bounds = 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
{
EnsureUndisposed ( ) ;
return implementation . ClientRectangle ;
}
set
{
EnsureUndisposed ( ) ;
implementation . 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
{
EnsureUndisposed ( ) ;
return implementation . ClientSize ;
}
set
{
EnsureUndisposed ( ) ;
implementation . ClientSize = value ;
}
}
#endregion
#region Exists
/// <summary>
/// Gets a value indicating whether a render window exists.
/// </summary>
public bool Exists
{
get
{
return disposed ? false : implementation . Exists ; // TODO: Should disposed be ignored instead?
}
}
#endregion
#region Focused
/// <summary>
/// Gets a System.Boolean that indicates whether this NativeWindow has input focus.
/// </summary>
public bool Focused
{
get
{
EnsureUndisposed ( ) ;
return implementation . Focused ;
}
}
#endregion
#region Height
/// <summary>
/// Gets or sets the external height of this window.
/// </summary>
public int Height
{
get
{
EnsureUndisposed ( ) ;
return implementation . Height ;
}
set
{
EnsureUndisposed ( ) ;
implementation . Height = value ;
}
}
#endregion
#region InputDriver
/// <summary>
/// This property is deprecated.
/// </summary>
[Obsolete]
public IInputDriver InputDriver
{
get
{
EnsureUndisposed ( ) ;
return implementation . InputDriver ;
}
}
#endregion
#region Location
/// <summary>
/// Gets or sets a <see cref="System.Drawing.Point"/> structure that contains the location of this window on the desktop.
/// </summary>
public Point Location
{
get
{
EnsureUndisposed ( ) ;
return implementation . Location ;
}
set
{
EnsureUndisposed ( ) ;
implementation . Location = value ;
}
}
#endregion
#region Size
/// <summary>
/// Gets or sets a <see cref="System.Drawing.Size"/> structure that contains the external size of this window.
/// </summary>
public Size Size
{
get
{
EnsureUndisposed ( ) ;
return implementation . Size ;
}
set
{
EnsureUndisposed ( ) ;
implementation . Size = value ;
}
}
#endregion
#region Title
/// <summary>
/// Gets or sets the NativeWindow title.
/// </summary>
public string Title
{
get
{
EnsureUndisposed ( ) ;
return implementation . Title ;
}
set
{
EnsureUndisposed ( ) ;
implementation . Title = value ;
}
}
#endregion
#region Visible
/// <summary>
/// Gets or sets a System.Boolean that indicates whether this NativeWindow is visible.
/// </summary>
public bool Visible
{
get
{
EnsureUndisposed ( ) ;
return implementation . Visible ;
}
set
{
EnsureUndisposed ( ) ;
implementation . Visible = value ;
}
}
#endregion
#region Width
/// <summary>
/// Gets or sets the external width of this window.
/// </summary>
public int Width
{
get
{
EnsureUndisposed ( ) ;
return implementation . Width ;
}
set
{
EnsureUndisposed ( ) ;
implementation . Width = value ;
}
}
#endregion
#region WindowBorder
/// <summary>
/// Gets or states the border of the NativeWindow.
/// </summary>
public WindowBorder WindowBorder
{
get
{
return implementation . WindowBorder ;
}
set
{
implementation . WindowBorder = value ;
}
}
#endregion
#region WindowInfo
/// <summary>
/// Gets the <see cref="OpenTK.Platform.IWindowInfo"/> of this window.
/// </summary>
public IWindowInfo WindowInfo
{
get
{
EnsureUndisposed ( ) ;
return implementation . WindowInfo ;
}
}
#endregion
#region WindowState
/// <summary>
/// Gets or states the state of the NativeWindow.
/// </summary>
public WindowState WindowState
{
get
{
return implementation . WindowState ;
}
set
{
implementation . WindowState = value ;
}
}
#endregion
#region X
/// <summary>
/// Gets or sets the horizontal location of this window on the desktop.
/// </summary>
public int X
{
get
{
EnsureUndisposed ( ) ;
return implementation . X ;
}
set
{
EnsureUndisposed ( ) ;
implementation . X = value ;
}
}
#endregion
#region Y
/// <summary>
/// Gets or sets the vertical location of this window on the desktop.
/// </summary>
public int Y
{
get
{
EnsureUndisposed ( ) ;
return implementation . Y ;
}
set
{
EnsureUndisposed ( ) ;
implementation . Y = value ;
}
}
#endregion
#endregion
#region Events
/// <summary>
/// Occurs after the window has closed.
/// </summary>
public event EventHandler < EventArgs > Closed ;
/// <summary>
/// Occurs when the window is about to close.
/// </summary>
public event EventHandler < CancelEventArgs > Closing ;
/// <summary>
/// Occurs when the window is disposed.
/// </summary>
public event EventHandler < EventArgs > Disposed ;
/// <summary>
/// Occurs when the <see cref="Focused"/> property of the window changes.
/// </summary>
public event EventHandler < EventArgs > FocusedChanged ;
/// <summary>
/// Occurs whenever a character is typed.
/// </summary>
public event EventHandler < KeyPressEventArgs > KeyPress ;
/// <summary>
/// Occurs whenever the window is moved.
/// </summary>
public event EventHandler < EventArgs > Move ;
/// <summary>
/// Occurs whenever the window is resized.
/// </summary>
public event EventHandler < EventArgs > Resize ;
/// <summary>
/// Occurs when the <see cref="Title"/> property of the window changes.
/// </summary>
public event EventHandler < EventArgs > TitleChanged ;
/// <summary>
/// Occurs when the <see cref="Visible"/> property of the window changes.
/// </summary>
public event EventHandler < EventArgs > VisibleChanged ;
/// <summary>
/// Occurs when the <see cref="WindowBorder"/> property of the window changes.
/// </summary>
public event EventHandler < EventArgs > WindowBorderChanged ;
/// <summary>
/// Occurs when the <see cref="WindowState"/> property of the window changes.
/// </summary>
public event EventHandler < EventArgs > WindowStateChanged ;
#endregion
#endregion
#region - - - IDisposable Members - - -
#region Dispose
/// <summary>
/// Releases all non-managed resources belonging to this NativeWindow.
/// </summary>
2009-09-04 23:27:05 +02:00
public virtual void Dispose ( )
2009-09-03 21:01:11 +02:00
{
if ( ! disposed )
{
2009-09-04 23:27:05 +02:00
if ( ( options & GameWindowFlags . Fullscreen ) ! = 0 )
{
//if (WindowState == WindowState.Fullscreen) WindowState = WindowState.Normal; // TODO: Revise.
device . RestoreResolution ( ) ;
}
2009-09-03 21:01:11 +02:00
implementation . Dispose ( ) ;
GC . SuppressFinalize ( this ) ;
disposed = true ;
}
}
#endregion
#endregion
#region - - - Protected Members - - -
#region Methods
#region EnsureUndisposed
/// <summary>
/// Ensures that this NativeWindow has not been disposed.
/// </summary>
/// <exception cref="System.ObjectDisposedException">
/// If this NativeWindow has been disposed.
/// </exception>
protected void EnsureUndisposed ( )
{
if ( disposed ) throw new ObjectDisposedException ( GetType ( ) . Name ) ;
}
#endregion
#region IsDisposed
protected bool IsDisposed ( ) //TODO: Could be a property but with a different name than the variable because of the event. Alternatively the disposed variable could be renamed too.
{
return disposed ;
}
#endregion
#region OnClosed
/// <summary>
/// Called when the NativeWindow has closed.
/// </summary>
/// <param name="e">Not used.</param>
2009-09-04 23:27:05 +02:00
protected virtual void OnClosed ( EventArgs e )
{
if ( Closed ! = null ) Closed ( this , e ) ;
}
2009-09-03 21:01:11 +02:00
#endregion
#region OnClosing
/// <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 NativeWindow from closing.</param>
2009-09-04 23:27:05 +02:00
protected virtual void OnClosing ( CancelEventArgs e )
{
if ( Closing ! = null ) Closing ( this , e ) ;
}
#endregion
#region OnDisposed
/// <summary>
/// Called when the NativeWindow is disposed.
/// </summary>
/// <param name="e">Not used.</param>
protected virtual void OnDisposed ( EventArgs e )
{
if ( Disposed ! = null ) Disposed ( this , e ) ;
}
#endregion
#region OnFocusedChanged
/// <summary>
/// Called when the <see cref="OpenTK.INativeWindow.Focused"/> property of the NativeWindow has changed.
/// </summary>
/// <param name="e">Not used.</param>
protected virtual void OnFocusedChanged ( EventArgs e )
{
if ( FocusedChanged ! = null ) FocusedChanged ( this , e ) ;
}
#endregion
#region OnKeyPress
/// <summary>
/// Called when a character is typed.
/// </summary>
/// <param name="e">The <see cref="OpenTK.KeyPressEventArgs"/> for this event.</param>
protected virtual void OnKeyPress ( KeyPressEventArgs e )
{
if ( KeyPress ! = null ) KeyPress ( this , e ) ;
}
2009-09-03 21:01:11 +02:00
#endregion
#region OnMove
/// <summary>
/// Called when the NativeWindow is moved.
/// </summary>
/// <param name="e">Not used.</param>
2009-09-04 23:27:05 +02:00
protected virtual void OnMove ( EventArgs e )
{
if ( Move ! = null ) Move ( this , e ) ;
}
2009-09-03 21:01:11 +02:00
#endregion
#region OnResize
/// <summary>
/// Called when the NativeWindow is resized.
/// </summary>
/// <param name="e">Not used.</param>
2009-09-04 23:27:05 +02:00
protected virtual void OnResize ( EventArgs e )
{
if ( Resize ! = null ) Resize ( this , e ) ;
}
#endregion
#region OnTitleChanged
/// <summary>
/// Called when the <see cref="OpenTK.INativeWindow.Title"/> property of the NativeWindow has changed.
/// </summary>
/// <param name="e">Not used.</param>
protected virtual void OnTitleChanged ( EventArgs e )
{
if ( TitleChanged ! = null ) TitleChanged ( this , e ) ;
}
#endregion
#region OnVisibleChanged
/// <summary>
/// Called when the <see cref="OpenTK.INativeWindow.Visible"/> property of the NativeWindow has changed.
/// </summary>
/// <param name="e">Not used.</param>
protected virtual void OnVisibleChanged ( EventArgs e )
{
if ( VisibleChanged ! = null ) VisibleChanged ( this , e ) ;
}
2009-09-03 21:01:11 +02:00
#endregion
#region OnWindowBorderChanged
/// <summary>
/// Called when the WindowBorder of this NativeWindow has changed.
/// </summary>
/// <param name="e">Not used.</param>
2009-09-04 23:27:05 +02:00
protected virtual void OnWindowBorderChanged ( EventArgs e )
{
if ( WindowBorderChanged ! = null ) WindowBorderChanged ( this , e ) ;
}
2009-09-03 21:01:11 +02:00
#endregion
#region OnWindowStateChanged
/// <summary>
/// Called when the WindowState of this NativeWindow has changed.
/// </summary>
/// <param name="e">Not used.</param>
2009-09-04 23:27:05 +02:00
protected virtual void OnWindowStateChanged ( EventArgs e )
{
if ( WindowStateChanged ! = null ) WindowStateChanged ( this , e ) ;
}
#endregion
#region ProcessEvents
/// <summary>
/// Processes operating system events until the NativeWindow becomes idle.
/// </summary>
/// <param name="retainEvents">If true, the state of underlying system event propagation will be preserved, otherwise event propagation will be enabled if it has not been already.</param>
protected void ProcessEvents ( bool retainEvents )
{
EnsureUndisposed ( ) ;
if ( ! events ) Events = true ;
implementation . ProcessEvents ( ) ;
}
2009-09-03 21:01:11 +02:00
#endregion
#endregion
#endregion
#region - - - Private Members - - -
2009-09-04 23:27:05 +02:00
#region Methods
2009-09-03 21:01:11 +02:00
#region OnClosedInternal
private void OnClosedInternal ( object sender , EventArgs e )
{
OnClosed ( e ) ;
2009-09-04 23:27:05 +02:00
Events = false ;
2009-09-03 21:01:11 +02:00
}
#endregion
#region OnClosingInternal
2009-09-04 23:27:05 +02:00
private void OnClosingInternal ( object sender , CancelEventArgs e ) { OnClosing ( e ) ; }
2009-09-03 21:01:11 +02:00
#endregion
#region OnDisposedInternal
2009-09-04 23:27:05 +02:00
private void OnDisposedInternal ( object sender , EventArgs e ) { OnDisposed ( e ) ; }
2009-09-03 21:01:11 +02:00
#endregion
#region OnFocusedChangedInternal
2009-09-04 23:27:05 +02:00
private void OnFocusedChangedInternal ( object sender , EventArgs e ) { OnFocusedChanged ( e ) ; }
2009-09-03 21:01:11 +02:00
#endregion
#region OnKeyPressInternal
2009-09-04 23:27:05 +02:00
private void OnKeyPressInternal ( object sender , KeyPressEventArgs e ) { OnKeyPress ( e ) ; }
2009-09-03 21:01:11 +02:00
#endregion
#region OnMoveInternal
2009-09-04 23:27:05 +02:00
private void OnMoveInternal ( object sender , EventArgs e ) { OnMove ( e ) ; }
2009-09-03 21:01:11 +02:00
#endregion
#region OnResizeInternal
2009-09-04 23:27:05 +02:00
private void OnResizeInternal ( object sender , EventArgs e ) { OnResize ( e ) ; }
2009-09-03 21:01:11 +02:00
#endregion
#region OnTitleChangedInternal
2009-09-04 23:27:05 +02:00
private void OnTitleChangedInternal ( object sender , EventArgs e ) { OnTitleChanged ( e ) ; }
2009-09-03 21:01:11 +02:00
#endregion
#region OnVisibleChangedInternal
2009-09-04 23:27:05 +02:00
private void OnVisibleChangedInternal ( object sender , EventArgs e ) { OnVisibleChanged ( e ) ; }
2009-09-03 21:01:11 +02:00
#endregion
#region OnWindowBorderChangedInternal
2009-09-04 23:27:05 +02:00
private void OnWindowBorderChangedInternal ( object sender , EventArgs e ) { OnWindowBorderChanged ( e ) ; }
2009-09-03 21:01:11 +02:00
#endregion
#region OnWindowStateChangedInternal
2009-09-04 23:27:05 +02:00
private void OnWindowStateChangedInternal ( object sender , EventArgs e ) { OnWindowStateChanged ( e ) ; }
2009-09-03 21:01:11 +02:00
2009-09-04 23:27:05 +02:00
#endregion
#endregion
#region Properties
#region Events
private bool Events
{
set
{
if ( value )
{
if ( events )
{
throw new InvalidOperationException ( "Event propagation is already enabled." ) ;
}
implementation . Closed + = OnClosedInternal ;
implementation . Closing + = OnClosingInternal ;
implementation . Disposed + = OnDisposedInternal ;
implementation . FocusedChanged + = OnFocusedChangedInternal ;
implementation . KeyPress + = OnKeyPressInternal ;
implementation . Move + = OnMoveInternal ;
implementation . Resize + = OnResizeInternal ;
implementation . TitleChanged + = OnTitleChangedInternal ;
implementation . VisibleChanged + = OnVisibleChangedInternal ;
implementation . WindowBorderChanged + = OnWindowBorderChangedInternal ;
implementation . WindowStateChanged + = OnWindowStateChangedInternal ;
events = true ;
}
else if ( events )
{
implementation . Closed - = OnClosedInternal ;
implementation . Closing - = OnClosingInternal ;
implementation . Disposed - = OnDisposedInternal ;
implementation . FocusedChanged - = OnFocusedChangedInternal ;
implementation . KeyPress - = OnKeyPressInternal ;
implementation . Move - = OnMoveInternal ;
implementation . Resize - = OnResizeInternal ;
implementation . TitleChanged - = OnTitleChangedInternal ;
implementation . VisibleChanged - = OnVisibleChangedInternal ;
implementation . WindowBorderChanged - = OnWindowBorderChangedInternal ;
implementation . WindowStateChanged - = OnWindowStateChangedInternal ;
events = false ;
}
else
{
throw new InvalidOperationException ( "Event propagation is already disabled." ) ;
}
}
2009-09-03 21:01:11 +02:00
}
#endregion
#endregion
2009-09-04 23:27:05 +02:00
#endregion
2009-09-03 21:01:11 +02:00
}
}