#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.
//
#endregion
using System;
using System.ComponentModel;
#if !MINIMAL
using System.Drawing;
#endif
using OpenTK.Graphics;
using OpenTK.Input;
using OpenTK.Platform;
namespace OpenTK
{
///
/// Instances of this class implement the interface on the current platform.
///
public class NativeWindow : INativeWindow
{
#region --- Fields ---
private readonly GameWindowFlags options;
private readonly DisplayDevice device;
private readonly INativeWindow implementation;
private bool disposed, events;
private bool cursor_visible = true;
private bool previous_cursor_visible = true;
#endregion
#region --- Contructors ---
/// Constructs a new NativeWindow with default attributes without enabling events.
public NativeWindow()
: this(640, 480, "OpenTK Native Window", GameWindowFlags.Default, GraphicsMode.Default, DisplayDevice.Default) { }
// TODO: Remaining constructors.
/// Constructs a new centered NativeWindow with the specified attributes.
/// The width of the NativeWindow in pixels.
/// The height of the NativeWindow in pixels.
/// The title of the NativeWindow.
/// GameWindow options specifying window appearance and behavior.
/// The OpenTK.Graphics.GraphicsMode of the NativeWindow.
/// The OpenTK.Graphics.DisplayDevice to construct the NativeWindow in.
/// If width or height is less than 1.
/// If mode or device is null.
public NativeWindow(int width, int height, string title, GameWindowFlags options, GraphicsMode mode, DisplayDevice device)
: this(device.Bounds.Left + (device.Bounds.Width - width) / 2,
device.Bounds.Top + (device.Bounds.Height - height) / 2,
width, height, title, options, mode, device) { }
/// Constructs a new NativeWindow with the specified attributes.
/// Horizontal screen space coordinate of the NativeWindow's origin.
/// Vertical screen space coordinate of the NativeWindow's origin.
/// The width of the NativeWindow in pixels.
/// The height of the NativeWindow in pixels.
/// The title of the NativeWindow.
/// GameWindow options specifying window appearance and behavior.
/// The OpenTK.Graphics.GraphicsMode of the NativeWindow.
/// The OpenTK.Graphics.DisplayDevice to construct the NativeWindow in.
/// If width or height is less than 1.
/// If mode or device is null.
public NativeWindow(int x, int y, int width, int height, string title, GameWindowFlags options, GraphicsMode mode, DisplayDevice device)
{
// 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");
this.options = options;
this.device = device;
implementation = Factory.Default.CreateNativeWindow(x, y, width, height, title, mode, options, this.device);
if ((options & GameWindowFlags.Fullscreen) != 0)
{
this.device.ChangeResolution(width, height, mode.ColorFormat.BitsPerPixel, 0);
WindowState = WindowState.Fullscreen;
}
}
#endregion
#region --- INativeWindow Members ---
#region Methods
#region Close
///
/// Closes the NativeWindow.
///
public void Close()
{
EnsureUndisposed();
implementation.Close();
}
#endregion
#region PointToClient
///
/// Transforms the specified point from screen to client coordinates.
///
///
/// A to transform.
///
///
/// The point transformed to client coordinates.
///
public Point PointToClient(Point point)
{
return implementation.PointToClient(point);
}
#endregion
#region PointToScreen
///
/// Transforms the specified point from client to screen coordinates.
///
///
/// A to transform.
///
///
/// The point transformed to screen coordinates.
///
public Point PointToScreen(Point point)
{
// Here we use the fact that PointToClient just translates the point, and PointToScreen
// should perform the inverse operation.
Point trans = PointToClient(Point.Empty);
point.X -= trans.X;
point.Y -= trans.Y;
return point;
}
#endregion
#region ProcessEvents
///
/// Processes operating system events until the NativeWindow becomes idle.
///
public void ProcessEvents()
{
ProcessEvents(false);
}
#endregion
#endregion
#region Properties
#region Bounds
///
/// Gets or sets a structure
/// that specifies the external bounds of this window, in screen coordinates.
/// The coordinates are specified in device-independent points and
/// include the title bar, borders and drawing area of the window.
///
public Rectangle Bounds
{
get
{
EnsureUndisposed();
return implementation.Bounds;
}
set
{
EnsureUndisposed();
implementation.Bounds = value;
}
}
#endregion
#region ClientRectangle
///
/// Gets or sets a structure
/// that defines the bounds of the OpenGL surface, in window coordinates.
/// The coordinates are specified in device-dependent pixels.
///
public Rectangle ClientRectangle
{
get
{
EnsureUndisposed();
return implementation.ClientRectangle;
}
set
{
EnsureUndisposed();
implementation.ClientRectangle = value;
}
}
#endregion
#region ClientSize
///
/// Gets or sets a structure
/// that defines the size of the OpenGL surface in window coordinates.
/// The coordinates are specified in device-dependent pixels.
///
public Size ClientSize
{
get
{
EnsureUndisposed();
return implementation.ClientSize;
}
set
{
EnsureUndisposed();
implementation.ClientSize = value;
}
}
#endregion
#region Exists
///
/// Gets a value indicating whether a render window exists.
///
public bool Exists
{
get
{
return IsDisposed ? false : implementation.Exists; // TODO: Should disposed be ignored instead?
}
}
#endregion
#region Focused
///
/// Gets a System.Boolean that indicates whether this NativeWindow has input focus.
///
public bool Focused
{
get
{
EnsureUndisposed();
return implementation.Focused;
}
}
#endregion
#region Height
///
/// Gets or sets the height of the OpenGL surface in window coordinates.
/// The coordinates are specified in device-dependent pixels.
///
public int Height
{
get
{
EnsureUndisposed();
return implementation.Height;
}
set
{
EnsureUndisposed();
implementation.Height = value;
}
}
#endregion
#region Icon
///
/// Gets or sets the System.Drawing.Icon for this GameWindow.
///
public Icon Icon
{
get
{
EnsureUndisposed();
return implementation.Icon;
}
set
{
EnsureUndisposed();
implementation.Icon = value;
}
}
#endregion
#region InputDriver
///
/// This property is deprecated.
///
[Obsolete]
public IInputDriver InputDriver
{
get
{
EnsureUndisposed();
return implementation.InputDriver;
}
}
#endregion
#region Location
///
/// Gets or sets a structure that contains the location of this window on the desktop.
///
public Point Location
{
get
{
EnsureUndisposed();
return implementation.Location;
}
set
{
EnsureUndisposed();
implementation.Location = value;
}
}
#endregion
#region Size
///
/// Gets or sets a structure that contains the external size of this window.
///
public Size Size
{
get
{
EnsureUndisposed();
return implementation.Size;
}
set
{
EnsureUndisposed();
implementation.Size = value;
}
}
#endregion
#region Title
///
/// Gets or sets the NativeWindow title.
///
public string Title
{
get
{
EnsureUndisposed();
return implementation.Title;
}
set
{
EnsureUndisposed();
implementation.Title = value;
}
}
#endregion
#region Visible
///
/// Gets or sets a System.Boolean that indicates whether this NativeWindow is visible.
///
public bool Visible
{
get
{
EnsureUndisposed();
return implementation.Visible;
}
set
{
EnsureUndisposed();
implementation.Visible = value;
}
}
#endregion
#region Width
///
/// Gets or sets the height of the OpenGL surface in window coordinates.
/// The coordinates are specified in device-dependent pixels.
///
public int Width
{
get
{
EnsureUndisposed();
return implementation.Width;
}
set
{
EnsureUndisposed();
implementation.Width = value;
}
}
#endregion
#region WindowBorder
///
/// Gets or states the border of the NativeWindow.
///
public WindowBorder WindowBorder
{
get
{
return implementation.WindowBorder;
}
set
{
implementation.WindowBorder = value;
}
}
#endregion
#region WindowInfo
///
/// Gets the of this window.
///
public IWindowInfo WindowInfo
{
get
{
EnsureUndisposed();
return implementation.WindowInfo;
}
}
#endregion
#region WindowState
///
/// Gets or states the state of the NativeWindow.
///
public virtual WindowState WindowState
{
get
{
return implementation.WindowState;
}
set
{
implementation.WindowState = value;
}
}
#endregion
#region X
///
/// Gets or sets the horizontal location of this window in screen coordinates.
/// The coordinates are specified in device-independent points.
///
public int X
{
get
{
EnsureUndisposed();
return implementation.X;
}
set
{
EnsureUndisposed();
implementation.X = value;
}
}
#endregion
#region Y
///
/// Gets or sets the vertical location of this window in screen coordinates.
/// The coordinates are specified in device-independent points.
///
public int Y
{
get
{
EnsureUndisposed();
return implementation.Y;
}
set
{
EnsureUndisposed();
implementation.Y = value;
}
}
#endregion
#region CursorVisible
///
/// Gets or sets a value indicating whether the mouse cursor is visible.
///
public bool CursorVisible
{
get { return cursor_visible; }
set
{
cursor_visible = value;
implementation.CursorVisible = value;
}
}
#endregion
#endregion
#region Events
///
/// Occurs after the window has closed.
///
public event EventHandler Closed = delegate { };
///
/// Occurs when the window is about to close.
///
public event EventHandler Closing = delegate { };
///
/// Occurs when the window is disposed.
///
public event EventHandler Disposed = delegate { };
///
/// Occurs when the property of the window changes.
///
public event EventHandler FocusedChanged = delegate { };
///
/// Occurs when the property of the window changes.
///
public event EventHandler IconChanged = delegate { };
///
/// Occurs whenever a keybord key is pressed.
///
public event EventHandler KeyDown = delegate { };
///
/// Occurs whenever a character is typed.
///
public event EventHandler KeyPress = delegate { };
///
/// Occurs whenever a keyboard key is released.
///
public event EventHandler KeyUp = delegate { };
///
/// Occurs whenever the window is moved.
///
public event EventHandler Move = delegate { };
///
/// Occurs whenever the mouse cursor enters the window .
///
public event EventHandler MouseEnter = delegate { };
///
/// Occurs whenever the mouse cursor leaves the window .
///
public event EventHandler MouseLeave = delegate { };
///
/// Occurs whenever the window is resized.
///
public event EventHandler Resize = delegate { };
///
/// Occurs when the property of the window changes.
///
public event EventHandler TitleChanged = delegate { };
///
/// Occurs when the property of the window changes.
///
public event EventHandler VisibleChanged = delegate { };
///
/// Occurs when the property of the window changes.
///
public event EventHandler WindowBorderChanged = delegate { };
///
/// Occurs when the property of the window changes.
///
public event EventHandler WindowStateChanged = delegate { };
#endregion
#endregion
#region --- IDisposable Members ---
#region Dispose
///
/// Releases all non-managed resources belonging to this NativeWindow.
///
public virtual void Dispose()
{
if (!IsDisposed)
{
if ((options & GameWindowFlags.Fullscreen) != 0)
{
//if (WindowState == WindowState.Fullscreen) WindowState = WindowState.Normal; // TODO: Revise.
device.RestoreResolution();
}
implementation.Dispose();
GC.SuppressFinalize(this);
IsDisposed = true;
}
}
#endregion
#endregion
#region --- Protected Members ---
#region Methods
#region EnsureUndisposed
///
/// Ensures that this NativeWindow has not been disposed.
///
///
/// If this NativeWindow has been disposed.
///
protected void EnsureUndisposed()
{
if (IsDisposed) throw new ObjectDisposedException(GetType().Name);
}
#endregion
#region IsDisposed
///
/// Gets or sets a , which indicates whether
/// this instance has been disposed.
///
protected bool IsDisposed
{
get { return disposed; }
set { disposed = value; }
}
#endregion
#region OnClosed
///
/// Called when the NativeWindow has closed.
///
/// Not used.
protected virtual void OnClosed(EventArgs e)
{
Closed(this, e);
}
#endregion
#region OnClosing
///
/// Called when the NativeWindow is about to close.
///
///
/// The for this event.
/// Set e.Cancel to true in order to stop the NativeWindow from closing.
protected virtual void OnClosing(CancelEventArgs e)
{
Closing(this, e);
}
#endregion
#region OnDisposed
///
/// Called when the NativeWindow is disposed.
///
/// Not used.
protected virtual void OnDisposed(EventArgs e)
{
Disposed(this, e);
}
#endregion
#region OnFocusedChanged
///
/// Called when the property of the NativeWindow has changed.
///
/// Not used.
protected virtual void OnFocusedChanged(EventArgs e)
{
if (!Focused)
{
// Release cursor when losing focus, to ensure
// IDEs continue working as expected.
previous_cursor_visible = CursorVisible;
CursorVisible = true;
}
else if (!previous_cursor_visible)
{
// Make cursor invisible when focus is regained
// if cursor was invisible on previous focus loss.
previous_cursor_visible = true;
CursorVisible = false;
}
FocusedChanged(this, e);
}
#endregion
#region OnIconChanged
///
/// Called when the property of the NativeWindow has changed.
///
/// Not used.
protected virtual void OnIconChanged(EventArgs e)
{
IconChanged(this, e);
}
#endregion
#region OnKeyDown
///
/// Occurs whenever a keybord key is pressed.
///
protected virtual void OnKeyDown(KeyboardKeyEventArgs e)
{
KeyDown(this, e);
}
#endregion
#region OnKeyPress
///
/// Called when a character is typed.
///
/// The for this event.
protected virtual void OnKeyPress(KeyPressEventArgs e)
{
KeyPress(this, e);
}
#endregion
#region OnKeyUp
///
/// Called when a keybord key is released.
///
/// The for this event.
protected virtual void OnKeyUp(KeyboardKeyEventArgs e)
{
KeyUp(this, e);
}
#endregion
#region OnMove
///
/// Called when the NativeWindow is moved.
///
/// Not used.
protected virtual void OnMove(EventArgs e)
{
Move(this, e);
}
#endregion
#region OnMouseEnter
///
/// Called whenever the mouse cursor reenters the window .
///
/// Not used.
protected virtual void OnMouseEnter(EventArgs e)
{
MouseEnter(this, e);
}
#endregion
#region OnMouseLeave
///
/// Called whenever the mouse cursor leaves the window .
///
/// Not used.
protected virtual void OnMouseLeave(EventArgs e)
{
MouseLeave(this, e);
}
#endregion
#region OnResize
///
/// Called when the NativeWindow is resized.
///
/// Not used.
protected virtual void OnResize(EventArgs e)
{
Resize(this, e);
}
#endregion
#region OnTitleChanged
///
/// Called when the property of the NativeWindow has changed.
///
/// Not used.
protected virtual void OnTitleChanged(EventArgs e)
{
TitleChanged(this, e);
}
#endregion
#region OnVisibleChanged
///
/// Called when the property of the NativeWindow has changed.
///
/// Not used.
protected virtual void OnVisibleChanged(EventArgs e)
{
VisibleChanged(this, e);
}
#endregion
#region OnWindowBorderChanged
///
/// Called when the WindowBorder of this NativeWindow has changed.
///
/// Not used.
protected virtual void OnWindowBorderChanged(EventArgs e)
{
WindowBorderChanged(this, e);
}
#endregion
#region OnWindowStateChanged
///
/// Called when the WindowState of this NativeWindow has changed.
///
/// Not used.
protected virtual void OnWindowStateChanged(EventArgs e)
{
WindowStateChanged(this, e);
}
#endregion
#region ProcessEvents
///
/// Processes operating system events until the NativeWindow becomes idle.
///
/// If true, the state of underlying system event propagation will be preserved, otherwise event propagation will be enabled if it has not been already.
protected void ProcessEvents(bool retainEvents)
{
EnsureUndisposed();
if (!retainEvents && !events) Events = true;
implementation.ProcessEvents();
}
#endregion
#endregion
#endregion
#region --- Private Members ---
#region Methods
#region OnClosedInternal
private void OnClosedInternal(object sender, EventArgs e)
{
OnClosed(e);
Events = false;
}
#endregion
#region OnClosingInternal
private void OnClosingInternal(object sender, CancelEventArgs e) { OnClosing(e); }
#endregion
#region OnDisposedInternal
private void OnDisposedInternal(object sender, EventArgs e) { OnDisposed(e); }
#endregion
#region OnFocusedChangedInternal
private void OnFocusedChangedInternal(object sender, EventArgs e) { OnFocusedChanged(e); }
#endregion
#region OnIconChangedInternal
private void OnIconChangedInternal(object sender, EventArgs e) { OnIconChanged(e); }
#endregion
#region OnKeyPressInternal
private void OnKeyPressInternal(object sender, KeyPressEventArgs e) { OnKeyPress(e); }
#endregion
#region OnMouseEnterInternal
private void OnMouseEnterInternal(object sender, EventArgs e) { OnMouseEnter(e); }
#endregion
#region OnMouseLeaveInternal
private void OnMouseLeaveInternal(object sender, EventArgs e) { OnMouseLeave(e); }
#endregion
#region OnMoveInternal
private void OnMoveInternal(object sender, EventArgs e) { OnMove(e); }
#endregion
#region OnResizeInternal
private void OnResizeInternal(object sender, EventArgs e) { OnResize(e); }
#endregion
#region OnTitleChangedInternal
private void OnTitleChangedInternal(object sender, EventArgs e) { OnTitleChanged(e); }
#endregion
#region OnVisibleChangedInternal
private void OnVisibleChangedInternal(object sender, EventArgs e) { OnVisibleChanged(e); }
#endregion
#region OnWindowBorderChangedInternal
private void OnWindowBorderChangedInternal(object sender, EventArgs e) { OnWindowBorderChanged(e); }
#endregion
#region OnWindowStateChangedInternal
private void OnWindowStateChangedInternal(object sender, EventArgs e) { OnWindowStateChanged(e); }
#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.IconChanged += OnIconChangedInternal;
implementation.KeyPress += OnKeyPressInternal;
implementation.MouseEnter += OnMouseEnterInternal;
implementation.MouseLeave += OnMouseLeaveInternal;
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.IconChanged -= OnIconChangedInternal;
implementation.KeyPress -= OnKeyPressInternal;
implementation.MouseEnter -= OnMouseEnterInternal;
implementation.MouseLeave -= OnMouseLeaveInternal;
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.");
}
}
}
#endregion
#endregion
#endregion
}
}