using System; using System.ComponentModel; using System.Drawing; 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; #endregion #region --- Contructors --- #region NativeWindow() /// Constructs a new NativeWindow with default attributes without enabling events. public NativeWindow() : this(640, 480, "OpenTK Native Window", GameWindowFlags.Default, GraphicsMode.Default, DisplayDevice.Default) { } #endregion // TODO: Remaining constructors. #region NativeWindow(int width, int height, string title, GameWindowFlags options, GraphicsMode mode, DisplayDevice device) /// 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) { } #endregion #region NativeWindow(int x, int y, int width, int height, string title, GameWindowFlags options, GraphicsMode mode, DisplayDevice 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 #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 System.Drawing.Point PointToClient(System.Drawing.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 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 /// /// 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 contains the external bounds of this window, in screen coordinates. /// External bounds 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 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. /// public Rectangle ClientRectangle { get { EnsureUndisposed(); return implementation.ClientRectangle; } set { EnsureUndisposed(); implementation.ClientRectangle = value; } } #endregion #region ClientSize /// /// Gets or sets a structure that contains the internal size this window. /// 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 disposed ? 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 external height of this window. /// public int Height { get { EnsureUndisposed(); return implementation.Height; } set { EnsureUndisposed(); implementation.Height = 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 external width of this window. /// 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 WindowState WindowState { get { return implementation.WindowState; } set { implementation.WindowState = value; } } #endregion #region X /// /// Gets or sets the horizontal location of this window on the desktop. /// 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 on the desktop. /// public int Y { get { EnsureUndisposed(); return implementation.Y; } set { EnsureUndisposed(); implementation.Y = value; } } #endregion #endregion #region Events /// /// Occurs after the window has closed. /// public event EventHandler Closed; /// /// Occurs when the window is about to close. /// public event EventHandler Closing; /// /// Occurs when the window is disposed. /// public event EventHandler Disposed; /// /// Occurs when the property of the window changes. /// public event EventHandler FocusedChanged; /// /// Occurs whenever a character is typed. /// public event EventHandler KeyPress; /// /// Occurs whenever the window is moved. /// public event EventHandler Move; /// /// Occurs whenever the window is resized. /// public event EventHandler Resize; /// /// Occurs when the property of the window changes. /// public event EventHandler TitleChanged; /// /// Occurs when the property of the window changes. /// public event EventHandler VisibleChanged; /// /// Occurs when the property of the window changes. /// public event EventHandler WindowBorderChanged; /// /// Occurs when the property of the window changes. /// public event EventHandler WindowStateChanged; #endregion #endregion #region --- IDisposable Members --- #region Dispose /// /// Releases all non-managed resources belonging to this NativeWindow. /// public virtual void Dispose() { if (!disposed) { if ((options & GameWindowFlags.Fullscreen) != 0) { //if (WindowState == WindowState.Fullscreen) WindowState = WindowState.Normal; // TODO: Revise. device.RestoreResolution(); } implementation.Dispose(); GC.SuppressFinalize(this); disposed = 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 (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 /// /// Called when the NativeWindow has closed. /// /// Not used. protected virtual void OnClosed(EventArgs e) { if (Closed != null) 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) { if (Closing != null) Closing(this, e); } #endregion #region OnDisposed /// /// Called when the NativeWindow is disposed. /// /// Not used. protected virtual void OnDisposed(EventArgs e) { if (Disposed != null) Disposed(this, e); } #endregion #region OnFocusedChanged /// /// Called when the property of the NativeWindow has changed. /// /// Not used. protected virtual void OnFocusedChanged(EventArgs e) { if (FocusedChanged != null) FocusedChanged(this, e); } #endregion #region OnKeyPress /// /// Called when a character is typed. /// /// The for this event. protected virtual void OnKeyPress(KeyPressEventArgs e) { if (KeyPress != null) KeyPress(this, e); } #endregion #region OnMove /// /// Called when the NativeWindow is moved. /// /// Not used. protected virtual void OnMove(EventArgs e) { if (Move != null) Move(this, e); } #endregion #region OnResize /// /// Called when the NativeWindow is resized. /// /// Not used. protected virtual void OnResize(EventArgs e) { if (Resize != null) Resize(this, e); } #endregion #region OnTitleChanged /// /// Called when the property of the NativeWindow has changed. /// /// Not used. protected virtual void OnTitleChanged(EventArgs e) { if (TitleChanged != null) TitleChanged(this, e); } #endregion #region OnVisibleChanged /// /// Called when the property of the NativeWindow has changed. /// /// Not used. protected virtual void OnVisibleChanged(EventArgs e) { if (VisibleChanged != null) VisibleChanged(this, e); } #endregion #region OnWindowBorderChanged /// /// Called when the WindowBorder of this NativeWindow has changed. /// /// Not used. protected virtual void OnWindowBorderChanged(EventArgs e) { if (WindowBorderChanged != null) WindowBorderChanged(this, e); } #endregion #region OnWindowStateChanged /// /// Called when the WindowState of this NativeWindow has changed. /// /// Not used. protected virtual void OnWindowStateChanged(EventArgs e) { if (WindowStateChanged != null) 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 (!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 OnKeyPressInternal private void OnKeyPressInternal(object sender, KeyPressEventArgs e) { OnKeyPress(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.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."); } } } #endregion #endregion #endregion } }