diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index 2fa96467..def1ff3f 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -29,11 +29,11 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; +using System.Drawing; using System.Threading; using OpenTK.Graphics; using OpenTK.Input; using OpenTK.Platform; -using System.Drawing; namespace OpenTK { @@ -69,16 +69,20 @@ namespace OpenTK /// parameters that /// specify the logic update rate, and the render update rate. /// - public class GameWindow : IGameWindow + public class GameWindow : NativeWindow0, IGameWindow, IDisposable { #region --- Fields --- - INativeWindow glWindow; - //DisplayMode mode; + //DisplayMode mode; // TODO: Removable? + + object exit_lock = new object(); + + IGraphicsContext glContext; - bool isExiting = false; bool hasMainLoop; - bool disposed; + bool isExiting = false; + + int main_loop_thread_id; double update_period, render_period; double target_update_period, target_render_period; @@ -89,18 +93,13 @@ namespace OpenTK //InputDriver input_driver; - IGraphicsContext glContext; - - int main_loop_thread_id; - object exit_lock = new object(); - #endregion #region --- Contructors --- #region public GameWindow() - /// Constructs a new GameWindow with sensible default attributes.. + /// Constructs a new GameWindow with sensible default attributes. public GameWindow() : this(640, 480, GraphicsMode.Default, "OpenTK Game Window", 0, DisplayDevice.Default) { } @@ -169,7 +168,7 @@ namespace OpenTK #region public GameWindow(int width, int height, GraphicsMode mode, string title, GameWindowFlags options, DisplayDevice device, int major, int minor, GraphicsContextFlags flags) - /// Constructs a new GameWindow with the specified attributes. + /// Constructs a new GameWindow with the specified attributes. /// The width of the GameWindow in pixels. /// The height of the GameWindow in pixels. /// The OpenTK.Graphics.GraphicsMode of the GameWindow. @@ -200,305 +199,68 @@ namespace OpenTK /// The GraphicsContextFlags version for the OpenGL GraphicsContext. /// An IGraphicsContext to share resources with. public GameWindow(int width, int height, GraphicsMode mode, string title, GameWindowFlags options, DisplayDevice device, - int major, int minor, GraphicsContextFlags flags, IGraphicsContext sharedContext) + int major, int minor, GraphicsContextFlags flags, IGraphicsContext sharedContext) + : base(width, height, title, options, + mode == null ? GraphicsMode.Default : mode, + device == null ? DisplayDevice.Default : device) { - if (width <= 0) - throw new ArgumentOutOfRangeException("width", "Must be greater than zero."); - if (height <= 0) - throw new ArgumentOutOfRangeException("height", "Must be greater than zero."); - if (mode == null) - mode = GraphicsMode.Default; - if (device == null) - device = DisplayDevice.Default; - try { - Rectangle window_bounds = new Rectangle(); - window_bounds.X = device.Bounds.Left + (device.Bounds.Width - width) / 2; - window_bounds.Y = device.Bounds.Top + (device.Bounds.Height - height) / 2; - window_bounds.Width = width; - window_bounds.Height = height; - glWindow = Platform.Factory.Default.CreateNativeWindow( - window_bounds.X, window_bounds.Y, - window_bounds.Width, window_bounds.Height, - title, mode, options, device); - - glContext = new GraphicsContext(mode, glWindow.WindowInfo, major, minor, flags); - glContext.MakeCurrent(this.WindowInfo); + glContext = new GraphicsContext(mode == null ? GraphicsMode.Default : mode, WindowInfo, major, minor, flags); + glContext.MakeCurrent(WindowInfo); (glContext as IGraphicsContextInternal).LoadAll(); - if ((options & GameWindowFlags.Fullscreen) != 0) - { - device.ChangeResolution(width, height, mode.ColorFormat.BitsPerPixel, 0); - this.WindowState = WindowState.Fullscreen; - } + VSync = VSyncMode.On; - this.VSync = VSyncMode.On; - - glWindow.Move += delegate(object sender, EventArgs e) { OnMoveInternal(e); }; - glWindow.Resize += delegate(object sender, EventArgs e) { OnResizeInternal(e); }; - glWindow.Closing += delegate(object sender, CancelEventArgs e) { OnClosingInternal(e); }; - glWindow.Closed += delegate(object sender, EventArgs e) { OnClosedInternal(e); }; //glWindow.WindowInfoChanged += delegate(object sender, EventArgs e) { OnWindowInfoChangedInternal(e); }; - glWindow.WindowBorderChanged += delegate(object sender, EventArgs e) { OnWindowBorderChangedInternal(e); }; - glWindow.WindowStateChanged += delegate(object sender, EventArgs e) { OnWindowStateChangedInternal(e); }; + EnableEvents(); } catch (Exception e) { Debug.Print(e.ToString()); - if (glWindow != null) - glWindow.Dispose(); + base.Dispose(); throw; } } #endregion - - #endregion - - #region --- Private Methods --- - - #region void ExitInternal() - - // Stops the main loop, if one exists. - void ExitInternal() - { - if (HasMainLoop) - { - throw new GameWindowExitException(); - } - } - - #region void ExitAsync() - - // Gracefully exits the GameWindow. May be called from any thread. - void ExitAsync() - { - HasMainLoop = false; - isExiting = true; - //UpdateFrame += delegate - //{ - // ExitInternal(); - //}; - } - - #endregion - - #endregion - - #region bool MustResize - - bool MustResize - { - get { return glWindow.Width != this.Width || glWindow.Height != this.Height; } - } - - #endregion - - #region bool HasMainLoop - - bool HasMainLoop - { - get { return hasMainLoop; } - set { hasMainLoop = value; } - } - - #endregion - - #region OnMoveInternal - - // Calls OnMove and raises the Move event. - void OnMoveInternal(EventArgs e) - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - if (!this.Exists || this.IsExiting) - return; - - OnMove(e); - - if (Move != null) - Move(this, e); - } - - #endregion - - #region OnResizeInternal - - // Calls OnResize and raises the Resize event. - void OnResizeInternal(EventArgs e) - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - if (!this.Exists || this.IsExiting) - return; - - OnResize(e); - - if (Resize != null) - Resize(this, e); - } - - #endregion - - #region OnClosingInternal - - void OnClosingInternal(CancelEventArgs e) - { - OnClosing(e); - - if (Closing != null) - Closing(this, e); - - if (!e.Cancel) - ExitAsync(); - } - - #endregion - - #region OnClosedInternal - - void OnClosedInternal(EventArgs e) - { - OnClosed(e); - - if (Closed != null) - Closed(this, e); - } - - #endregion - - #region OnWindowInfoChangedInternal - - void OnWindowInfoChangedInternal(EventArgs e) - { - glContext.MakeCurrent(WindowInfo); - - OnWindowInfoChanged(e); - } - - #endregion - - #region OnWindowBorderChangedInternal - - void OnWindowBorderChangedInternal(EventArgs e) - { - OnWindowBorderChanged(e); - - WindowBorderChanged(this, EventArgs.Empty); - } - - #endregion - - #region OnWindowStateChangedInternal - - void OnWindowStateChangedInternal(EventArgs e) - { - OnWindowStateChanged(e); - - WindowStateChanged(this, e); - } - - #endregion - - #region OnUpdateFrameInternal - - private void OnUpdateFrameInternal(FrameEventArgs e) - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - if (!this.Exists || this.IsExiting) - return; - - if (UpdateFrame != null) - UpdateFrame(this, e); - - OnUpdateFrame(e); - } - - #endregion - - #region OnRenderFrameInternal - - private void OnRenderFrameInternal(FrameEventArgs e) - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - if (!this.Exists || this.IsExiting) - return; - - if (RenderFrame != null) - RenderFrame(this, e); - - OnRenderFrame(e); - } - - #endregion - - #endregion - - #region --- Protected Members --- - - /// - /// Called when the GameWindow is moved. - /// - /// Not used. - protected virtual void OnMove(EventArgs e) - { } - - /// - /// Called when the GameWindow is resized. - /// - /// Not used. - protected virtual void OnResize(EventArgs e) - { } - - /// - /// Called when the GameWindow is about to close. - /// - /// - /// The for this event. - /// Set e.Cancel to true in order to stop the GameWindow from closing. - protected virtual void OnClosing(CancelEventArgs e) - { } - - /// - /// Called when the GameWindow has closed. - /// - /// Not used. - protected virtual void OnClosed(EventArgs e) - { } - - /// - /// Called when the WindowInfo for this GameWindow has changed. - /// - /// Not used. - protected virtual void OnWindowInfoChanged(EventArgs e) - { } - - /// - /// Called when the WindowBorder for this GameWindow has changed. - /// - /// Not used. - protected virtual void OnWindowBorderChanged(EventArgs e) - { } - - /// - /// Called when the WindowState for this GameWindow has changed. - /// - /// Not used. - protected virtual void OnWindowStateChanged(EventArgs e) - { } #endregion #region --- Public Members --- - #region public virtual void Exit() + #region Methods + + #region Dispose + + /// + /// Disposes of the GameWindow, releasing all resources consumed by it. + /// + public new void Dispose() + { + try + { + Dispose(true); + } + finally + { + if (!IsDisposed()) + { + if (glContext != null) + { + glContext.Dispose(); + glContext = null; + } + + base.Dispose(); + } + } + GC.SuppressFinalize(this); + } + + #endregion + + #region Exit /// /// Gracefully exits the GameWindow. May be called from any thread. @@ -511,18 +273,17 @@ namespace OpenTK { lock (exit_lock) { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); + EnsureUndisposed(); if (!IsExiting && Exists) { CancelEventArgs e = new CancelEventArgs(); - OnClosingInternal(e); + OnClosing(e); if (e.Cancel) return; isExiting = true; - + if (HasMainLoop) { if (main_loop_thread_id == Thread.CurrentThread.ManagedThreadId) @@ -536,83 +297,43 @@ namespace OpenTK #endregion - #region public IGraphicsContext Context + #region MakeCurrent /// - /// Returns the opengl IGraphicsContext associated with the current GameWindow. + /// Makes the GraphicsContext current on the calling thread. /// - public IGraphicsContext Context + public void MakeCurrent() { - get - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - return glContext; - } + EnsureUndisposed(); + Context.MakeCurrent(WindowInfo); } #endregion - #region public bool Exists + #region OnLoad /// - /// Gets a value indicating whether a render window exists. + /// Occurs after establishing an OpenGL context, but before entering the main loop. + /// Override to load resources that should be maintained for the lifetime of the application. /// - public bool Exists + /// Not used. + public virtual void OnLoad(EventArgs e) { - get { return glWindow == null ? false : glWindow.Exists; } + EnsureUndisposed(); //if (disposed) throw new ObjectDisposedException("GameWindow"); // What is the exact purpose? } #endregion - #region public string Text + #region OnUnload /// - /// Gets or sets the GameWindow title. + /// Occurs after after calling GameWindow.Exit, but before destroying the OpenGL context. + /// Override to unload application resources. /// - public string Title + /// Not used. + public virtual void OnUnload(EventArgs e) { - get - { - if (disposed) throw new ObjectDisposedException(this.GetType().Name); - return glWindow.Title; - } - set - { - if (disposed) throw new ObjectDisposedException(this.GetType().Name); - glWindow.Title = value; - } - } - - #endregion - - #region public bool Visible -#if false - /// - /// TODO: This property is not implemented - /// Gets or sets a value indicating whether the GameWindow is visible. - /// - public bool Visible - { - get - { - throw new NotImplementedException(); - //return glWindow.Visible; - } - set - { - throw new NotImplementedException(); - //glWindow.Visible = value; - } - } -#endif - #endregion - - #region public IWindowInfo WindowInfo - - public IWindowInfo WindowInfo - { - get { if (disposed) throw new ObjectDisposedException("GameWindow"); return glWindow.WindowInfo; } + EnsureUndisposed(); //if (disposed) throw new ObjectDisposedException("GameWindow"); // What is the exact purpose? } #endregion @@ -625,7 +346,6 @@ namespace OpenTK /// public void Run() { - if (disposed) throw new ObjectDisposedException(this.GetType().Name); Run(0.0, 0.0); } @@ -639,9 +359,6 @@ namespace OpenTK /// public void Run(double updateRate) { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - Run(updateRate, 0.0); } @@ -652,19 +369,25 @@ namespace OpenTK /// /// Enters the game loop of the GameWindow updating and rendering at the specified frequency. /// + /// + /// When overriding the default game loop you should call ProcessEvents() + /// to ensure that your GameWindow responds to operating system events. + /// + /// Once ProcessEvents() returns, it is time to call update and render the next frame. + /// + /// /// The frequency of UpdateFrame events. /// The frequency of RenderFrame events. public void Run(double updates_per_second, double frames_per_second) { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); + EnsureUndisposed(); try { // Necessary to be here, otherwise Exit() wouldn't work correctly when called inside OnLoad(). hasMainLoop = true; main_loop_thread_id = Thread.CurrentThread.ManagedThreadId; - + if (updates_per_second < 0.0 || updates_per_second > 200.0) throw new ArgumentOutOfRangeException("updates_per_second", updates_per_second, "Parameter should be inside the range [0.0, 200.0]"); @@ -685,7 +408,6 @@ namespace OpenTK render_watch.Reset(); OnLoadInternal(EventArgs.Empty); - OnResizeInternal(EventArgs.Empty); Debug.Print("Entering main loop."); while (!IsExiting && HasMainLoop) @@ -778,136 +500,48 @@ namespace OpenTK if (Exists) { - glContext.Dispose(); - glContext = null; - - glWindow.Dispose(); - while (this.Exists) - this.ProcessEvents(); - glWindow = null; + Dispose(); + //while (this.Exists) ProcessEvents(); // TODO: Should similar behaviour be retained, possibly on native window level? } } } #endregion - #region public void ProcessEvents() + #region SwapBuffers /// - /// Processes operating system events until the GameWindow becomes idle. + /// Swaps the front and back buffer, presenting the rendered scene to the user. /// - /// - /// 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. - /// - /// Once ProcessEvents() returns, it is time to call update and render the next frame. - /// - /// - public void ProcessEvents() + public void SwapBuffers() { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - glWindow.ProcessEvents(); + EnsureUndisposed(); + this.Context.SwapBuffers(); } #endregion - #region OnRenderFrame - - /// - /// Override in derived classes to render a frame. - /// - /// Contains information necessary for frame rendering. - /// - /// The base implementation (base.OnRenderFrame) is empty, there is no need to call it. - /// - protected virtual void OnRenderFrame(FrameEventArgs e) - { - } - - /// - /// Occurs when it is time to render a frame. - /// - public event EventHandler RenderFrame; - #endregion - #region OnUpdateFrame + #region Properties + + #region Context /// - /// Override in derived classes to update a frame. + /// Returns the opengl IGraphicsContext associated with the current GameWindow. /// - /// Contains information necessary for frame updating. - /// - /// The base implementation (base.OnUpdateFrame) is empty, there is no need to call it. - /// - protected virtual void OnUpdateFrame(FrameEventArgs e) + public IGraphicsContext Context { - } - - /// - /// Occurs when it is time to update a frame. - /// - public event EventHandler UpdateFrame; - - #endregion - - #region OnLoad - - /// - /// Raises the Load event, and calls the user's OnLoad override. - /// - /// - private void OnLoadInternal(EventArgs e) - { - OnResizeInternal(EventArgs.Empty); - Load(this, e); - OnLoad(e); - } - - /// - /// Occurs after establishing an OpenGL context, but before entering the main loop. - /// Override to load resources that should be maintained for the lifetime of the application. - /// - /// Not used. - public virtual void OnLoad(EventArgs e) - { - if (disposed) throw new ObjectDisposedException("GameWindow"); - } - - #endregion - - #region OnUnload - - /// - /// Raises the Unload event, and calls the user's OnUnload override. - /// - /// - private void OnUnloadInternal(EventArgs e) - { - if (this.Unload != null) + get { - this.Unload(this, e); + EnsureUndisposed(); + return glContext; } - - OnUnload(e); - } - - /// - /// Occurs after after calling GameWindow.Exit, but before destroying the OpenGL context. - /// Override to unload application resources. - /// - /// Not used. - public virtual void OnUnload(EventArgs e) - { - if (disposed) throw new ObjectDisposedException("GameWindow"); } #endregion - #region public bool IsExiting + #region IsExiting /// /// Gets a value indicating whether the shutdown sequence has been initiated @@ -919,53 +553,14 @@ namespace OpenTK { get { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); + EnsureUndisposed(); return isExiting; } } #endregion - #region public Keyboard Keyboard - - /// - /// Gets the primary Keyboard device, or null if no Keyboard exists. - /// - [Obsolete] - public KeyboardDevice Keyboard - { - get - { - if (InputDriver.Keyboard.Count > 0) - return InputDriver.Keyboard[0]; - else - return null; - } - } - - #endregion - - #region public Mouse Mouse - - /// - /// Gets the primary Mouse device, or null if no Mouse exists. - /// - [Obsolete] - public MouseDevice Mouse - { - get - { - if (InputDriver.Mouse.Count > 0) - return InputDriver.Mouse[0]; - else - return null; - } - } - - #endregion - - #region public IList Joysticks + #region Joysticks /// /// Gets a readonly IList containing all available OpenTK.Input.JoystickDevices. @@ -978,525 +573,32 @@ namespace OpenTK #endregion - #region public VSyncMode VSync + #region Keyboard /// - /// Gets or sets the VSyncMode. - /// - public VSyncMode VSync - { - get - { - if (disposed) - throw new ObjectDisposedException("GameWindow"); - - GraphicsContext.Assert(); - - return vsync; - } - set - { - if (disposed) - throw new ObjectDisposedException("GameWindow"); - - GraphicsContext.Assert(); - - if (value == VSyncMode.Off) - Context.VSync = false; - else - Context.VSync = true; - - vsync = value; - } - } - - #endregion - - #region MakeCurrent - - /// - /// Makes the GraphicsContext current on the calling thread. - /// - public void MakeCurrent() - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - Context.MakeCurrent(WindowInfo); - } - - #endregion - - #region public void SwapBuffers() - - /// - /// Swaps the front and back buffer, presenting the rendered scene to the user. - /// - public void SwapBuffers() - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - this.Context.SwapBuffers(); - } - - #endregion - - #region public WindowState WindowState - - /// - /// Gets or states the state of the GameWindow. - /// - public WindowState WindowState - { - get - { - return glWindow.WindowState; - } - set - { - glWindow.WindowState = value; - } - } - - #endregion - - #region public WindowBorder WindowBorder - - /// - /// Gets or states the border of the GameWindow. - /// - public WindowBorder WindowBorder - { - get - { - return glWindow.WindowBorder; - } - set - { - glWindow.WindowBorder = value; - } - } - - #endregion - - #endregion - - #region --- INativeWindow Members --- - - #region Focused - - /// - /// Gets a System.Boolean that indicates whether this GameWindow has input focus. - /// - public bool Focused - { - get - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - return glWindow.Focused; - } - } - - #endregion - - #region Visible - - /// - /// Gets or sets a System.Boolean that indicates whether this GameWindow is visible. - /// - public bool Visible - { - get - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - return glWindow.Visible; - } - set - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - glWindow.Visible = value; - } - } - - #endregion - - #region Bounds - - /// - /// Gets or sets a structure the 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 - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - return glWindow.Bounds; - } - set - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - glWindow.Bounds = value; - } - } - - #endregion - - #region Location - - /// - /// Gets or sets a structure that contains the location of this window on the desktop. - /// - public Point Location - { - get - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - return glWindow.Location; - } - set - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - glWindow.Location = value; - } - } - - #endregion - - #region Size - - /// - /// Gets or sets a structure that contains the external size of this window. - /// - public Size Size - { - get - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - return glWindow.Size; - } - set - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - glWindow.Size = value; - } - } - - #endregion - - #region X - - /// - /// Gets or sets the horizontal location of this window on the desktop. - /// - public int X - { - get - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - return glWindow.X; - } - set - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - glWindow.X = value; - } - } - - #endregion - - #region Y - - /// - /// Gets or sets the vertical location of this window on the desktop. - /// - public int Y - { - get - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - return glWindow.Y; - } - set - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - glWindow.Y = value; - } - } - - #endregion - - #region Width - - /// - /// Gets or sets the external width of this window. - /// - public int Width - { - get - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - return glWindow.Width; - } - set - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - glWindow.Width = value; - } - } - - #endregion - - #region Height - - /// - /// Gets or sets the external height of this window. - /// - public int Height - { - get - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - return glWindow.Height; - } - set - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - glWindow.Height = 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 - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - return glWindow.ClientRectangle; - } - set - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - glWindow.ClientRectangle = value; - } - } - - #endregion - - #region ClientSize - - /// - /// Gets or sets a structure that contains the internal size this window. - /// - public Size ClientSize - { - get - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - return glWindow.ClientSize; - } - set - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - glWindow.ClientSize = value; - } - } - - #endregion - - #region InputDriver - - /// - /// This property is deprecated. + /// Gets the primary Keyboard device, or null if no Keyboard exists. /// [Obsolete] - public IInputDriver InputDriver + public KeyboardDevice Keyboard { - get - { - if (disposed) - throw new ObjectDisposedException(this.GetType().Name); - - return glWindow.InputDriver; - } + get { return InputDriver.Keyboard.Count > 0 ? InputDriver.Keyboard[0] : null; } } #endregion - #region Close + #region Mouse /// - /// Closes the GameWindow. Equivalent to calling . + /// Gets the primary Mouse device, or null if no Mouse exists. /// - public void Close() + [Obsolete] + public MouseDevice Mouse { - Exit(); + get { return InputDriver.Mouse.Count > 0 ? InputDriver.Mouse[0] : null; } } #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 glWindow.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 Events - - /// - /// Occurs before the window is displayed for the first time. - /// - public event EventHandler Load = delegate { }; - - /// - /// Occurs before the window is destroyed. - /// - public event EventHandler Unload = delegate { }; - - /// - /// Occurs whenever the window is moved. - /// - public event EventHandler Move = delegate { }; - - /// - /// Occurs whenever the window is resized. - /// - public event EventHandler Resize = delegate { }; - - /// - /// Occurs when the window is about to close. - /// - public event EventHandler Closing = delegate { }; - - /// - /// Occurs after the window has closed. - /// - public event EventHandler Closed = delegate { }; - - /// - /// Occurs when the window is disposed. - /// - public event EventHandler Disposed = 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 FocusedChanged = 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 { }; - - /// - /// Occurs whenever a character is typed. - /// - public event EventHandler KeyPress = delegate { }; - - #endregion - - #endregion - #region --- GameWindow Timing --- // TODO: Disabled because it is not reliable enough. Use vsync as a workaround. @@ -1511,7 +613,97 @@ namespace OpenTK //#endregion - #region public double TargetRenderPeriod + #region RenderFrequency + + /// + /// Gets a double representing the actual frequency of RenderFrame events, in Herz (i.e. FPS or Frames Per Second). + /// + public double RenderFrequency + { + get + { + EnsureUndisposed(); + if (render_period == 0.0) + return 1.0; + return 1.0 / render_period; + } + } + + #endregion + + #region RenderPeriod + + /// + /// Gets a double representing the period of RenderFrame events, in seconds. + /// + public double RenderPeriod + { + get + { + EnsureUndisposed(); + return render_period; + } + } + + #endregion + + #region RenderTime + + /// + /// Gets a double representing the time spent in the RenderFrame function, in seconds. + /// + public double RenderTime + { + get + { + EnsureUndisposed(); + return render_time; + } + protected set + { + EnsureUndisposed(); + render_time = value; + } + } + + #endregion + + #region TargetRenderFrequency + + /// + /// Gets or sets a double representing the target render frequency, in Herz. + /// + /// + /// 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). + /// Values lower than 1.0Hz are clamped to 1.0Hz. Values higher than 200.0Hz are clamped to 200.0Hz. + /// + public double TargetRenderFrequency + { + get + { + EnsureUndisposed(); + if (TargetRenderPeriod == 0.0) + return 0.0; + return 1.0 / TargetRenderPeriod; + } + set + { + EnsureUndisposed(); + if (value < 1.0) + { + TargetRenderPeriod = 0.0; + } + else if (value <= 200.0) + { + TargetRenderPeriod = 1.0 / value; + } + else Debug.Print("Target render frequency clamped to 200.0Hz."); // TODO: Where is it actually performed? + } + } + + #endregion + + #region TargetRenderPeriod /// /// Gets or sets a double representing the target render period, in seconds. @@ -1524,12 +716,12 @@ namespace OpenTK { get { - if (disposed) throw new ObjectDisposedException("GameWindow"); + EnsureUndisposed(); return target_render_period; } set { - if (disposed) throw new ObjectDisposedException("GameWindow"); + EnsureUndisposed(); if (value <= 0.005) { target_render_period = 0.0; @@ -1544,75 +736,7 @@ namespace OpenTK #endregion - #region public double TargetRenderFrequency - - /// - /// Gets or sets a double representing the target render frequency, in Herz. - /// - /// - /// 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). - /// Values lower than 1.0Hz are clamped to 1.0Hz. Values higher than 200.0Hz are clamped to 200.0Hz. - /// - public double TargetRenderFrequency - { - get - { - if (disposed) throw new ObjectDisposedException("GameWindow"); - if (TargetRenderPeriod == 0.0) - return 0.0; - return 1.0 / TargetRenderPeriod; - } - set - { - if (disposed) throw new ObjectDisposedException("GameWindow"); - if (value < 1.0) - { - TargetRenderPeriod = 0.0; - } - else if (value <= 200.0) - { - TargetRenderPeriod = 1.0 / value; - } - else Debug.Print("Target render frequency clamped to 200.0Hz."); - } - } - - #endregion - - #region public double TargetUpdatePeriod - - /// - /// Gets or sets a double representing the target update period, in seconds. - /// - /// - /// 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). - /// Values lower than 0.005 seconds (200Hz) are clamped to 0.0. Values higher than 1.0 seconds (1Hz) are clamped to 1.0. - /// - public double TargetUpdatePeriod - { - get - { - if (disposed) throw new ObjectDisposedException("GameWindow"); - return target_update_period; - } - set - { - if (disposed) throw new ObjectDisposedException("GameWindow"); - if (value <= 0.005) - { - target_update_period = 0.0; - } - else if (value <= 1.0) - { - target_update_period = value; - } - else Debug.Print("Target update period clamped to 1.0 seconds."); - } - } - - #endregion - - #region public double TargetUpdateFrequency + #region TargetUpdateFrequency /// /// Gets or sets a double representing the target update frequency, in Herz. @@ -1625,14 +749,14 @@ namespace OpenTK { get { - if (disposed) throw new ObjectDisposedException("GameWindow"); + EnsureUndisposed(); if (TargetUpdatePeriod == 0.0) return 0.0; return 1.0 / TargetUpdatePeriod; } set { - if (disposed) throw new ObjectDisposedException("GameWindow"); + EnsureUndisposed(); if (value < 1.0) { TargetUpdatePeriod = 0.0; @@ -1641,47 +765,46 @@ namespace OpenTK { TargetUpdatePeriod = 1.0 / value; } - else Debug.Print("Target update frequency clamped to 200.0Hz."); + else Debug.Print("Target update frequency clamped to 200.0Hz."); // TODO: Where is it actually performed? } } #endregion - #region public double RenderFrequency + #region TargetUpdatePeriod /// - /// Gets a double representing the actual frequency of RenderFrame events, in Herz (i.e. FPS or Frames Per Second). + /// Gets or sets a double representing the target update period, in seconds. /// - public double RenderFrequency + /// + /// 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). + /// Values lower than 0.005 seconds (200Hz) are clamped to 0.0. Values higher than 1.0 seconds (1Hz) are clamped to 1.0. + /// + public double TargetUpdatePeriod { get { - if (disposed) throw new ObjectDisposedException("GameWindow"); - if (render_period == 0.0) - return 1.0; - return 1.0 / render_period; + EnsureUndisposed(); + return target_update_period; } - } - - #endregion - - #region public double RenderPeriod - - /// - /// Gets a double representing the period of RenderFrame events, in seconds. - /// - public double RenderPeriod - { - get + set { - if (disposed) throw new ObjectDisposedException("GameWindow"); - return render_period; + EnsureUndisposed(); + if (value <= 0.005) + { + target_update_period = 0.0; + } + else if (value <= 1.0) + { + target_update_period = value; + } + else Debug.Print("Target update period clamped to 1.0 seconds."); // TODO: Where is it actually performed? } } #endregion - #region public double UpdateFrequency + #region UpdateFrequency /// /// Gets a double representing the frequency of UpdateFrame events, in Herz. @@ -1690,7 +813,7 @@ namespace OpenTK { get { - if (disposed) throw new ObjectDisposedException("GameWindow"); + EnsureUndisposed(); if (update_period == 0.0) return 1.0; return 1.0 / update_period; @@ -1699,7 +822,7 @@ namespace OpenTK #endregion - #region public double UpdatePeriod + #region UpdatePeriod /// /// Gets a double representing the period of UpdateFrame events, in seconds. @@ -1708,88 +831,259 @@ namespace OpenTK { get { - if (disposed) throw new ObjectDisposedException("GameWindow"); + EnsureUndisposed(); return update_period; } } #endregion - #region public double RenderTime - - /// - /// Gets a double representing the time spent in the RenderFrame function, in seconds. - /// - public double RenderTime - { - get { if (disposed) throw new ObjectDisposedException("GameWindow"); return render_time; } - protected set { if (disposed) throw new ObjectDisposedException("GameWindow"); render_time = value; } - } - - #endregion - - #region public double RenderTime + #region UpdateTime /// /// Gets a double representing the time spent in the UpdateFrame function, in seconds. /// public double UpdateTime { - get { if (disposed) throw new ObjectDisposedException("GameWindow"); return update_time; } + get + { + EnsureUndisposed(); + return update_time; + } } #endregion #endregion - #region --- IDisposable Members --- + #region VSync /// - /// Disposes of the GameWindow, releasing all resources consumed by it. + /// Gets or sets the VSyncMode. /// - public void Dispose() + public VSyncMode VSync { - try + get { - Dispose(true); + EnsureUndisposed(); + GraphicsContext.Assert(); + return vsync; } - finally + set { - DisposeInternal(true); + EnsureUndisposed(); + GraphicsContext.Assert(); + Context.VSync = (vsync = value) != VSyncMode.Off; } - GC.SuppressFinalize(this); } - private void DisposeInternal(bool manual) - { - if (!disposed) - { - if (manual) - { - if (glContext != null) - { - glContext.Dispose(); - glContext = null; - } + #endregion - if (glWindow != null) - { - glWindow.Dispose(); - glWindow = null; - } - } - disposed = true; - } - } + #endregion + + #region Events + + /// + /// Occurs before the window is displayed for the first time. + /// + public event EventHandler Load; + + /// + /// Occurs when it is time to render a frame. + /// + public event EventHandler RenderFrame; + + /// + /// Occurs before the window is destroyed. + /// + public event EventHandler Unload; + + /// + /// Occurs when it is time to update a frame. + /// + public event EventHandler UpdateFrame; + + #endregion + + #endregion + + #region --- Protected Members --- + + #region Dispose /// /// Override to add custom cleanup logic. /// /// True, if this method was called by the application; false if this was called by the finalizer thread. - protected virtual void Dispose(bool manual) + protected virtual void Dispose(bool manual) { } + + #endregion + + #region OnRenderFrame + + /// + /// Override in derived classes to render a frame. + /// + /// Contains information necessary for frame rendering. + /// + /// The base implementation (base.OnRenderFrame) is empty, there is no need to call it. + /// + protected virtual void OnRenderFrame(FrameEventArgs e) { } + + #endregion + + #region OnUpdateFrame + + /// + /// Override in derived classes to update a frame. + /// + /// Contains information necessary for frame updating. + /// + /// The base implementation (base.OnUpdateFrame) is empty, there is no need to call it. + /// + protected virtual void OnUpdateFrame(FrameEventArgs e) { } + + #endregion + + #region OnWindowInfoChanged + + /// + /// Called when the WindowInfo for this GameWindow has changed. + /// + /// Not used. + protected virtual void OnWindowInfoChanged(EventArgs e) { } + + #endregion + + #endregion + + #region --- Assembly Members --- + + #region Methods + + #region ExitAsync + + // Gracefully exits the GameWindow. May be called from any thread. + void ExitAsync() { + HasMainLoop = false; + isExiting = true; + //UpdateFrame += delegate + //{ + // ExitInternal(); + //}; } + #endregion + + #region ExitInternal + + // Stops the main loop, if one exists. + void ExitInternal() + { + if (HasMainLoop) + { + throw new GameWindowExitException(); + } + } + + #endregion + + #endregion + + #region Properties + + #region HasMainLoop + + bool HasMainLoop + { + get { return hasMainLoop; } + set { hasMainLoop = value; } + } + + #endregion + + #endregion + + #endregion + + #region --- Private Members --- + + #region OnLoadInternal + + /// + /// Raises the Load event, and calls the user's OnLoad override. + /// + /// The event data. + private void OnLoadInternal(EventArgs e) + { + OnResize(EventArgs.Empty); + + if (Load != null) Load(this, e); + + OnLoad(e); + } + + #endregion + + #region OnRenderFrameInternal + + private void OnRenderFrameInternal(FrameEventArgs e) + { + EnsureUndisposed(); + + if (!this.Exists || this.IsExiting) return; // TODO: Redundant because of EnsureUndisposed. + + if (RenderFrame != null) RenderFrame(this, e); + + OnRenderFrame(e); + } + + #endregion + + #region OnUnloadInternal + + /// + /// Raises the Unload event, and calls the user's OnUnload override. + /// + /// The event data. + private void OnUnloadInternal(EventArgs e) + { + if (Unload != null) Unload(this, e); + + OnUnload(e); + } + + #endregion + + #region OnUpdateFrameInternal + + private void OnUpdateFrameInternal(FrameEventArgs e) + { + EnsureUndisposed(); + + if (!this.Exists || this.IsExiting) return; // TODO: Redundant because of EnsureUndisposed. + + if (UpdateFrame != null) UpdateFrame(this, e); + + OnUpdateFrame(e); + } + + #endregion + + #region OnWindowInfoChangedInternal + + private void OnWindowInfoChangedInternal(EventArgs e) + { + glContext.MakeCurrent(WindowInfo); + + OnWindowInfoChanged(e); + } + + #endregion + + #endregion + ///// Finalizes unmanaged resources consumed by the GameWindow. //~GameWindow() //{ @@ -1797,7 +1091,6 @@ namespace OpenTK // DisposeInternal(false); //} - #endregion } #region public enum VSyncMode diff --git a/Source/OpenTK/NativeWindow.cs b/Source/OpenTK/NativeWindow.cs new file mode 100644 index 00000000..73b57a62 --- /dev/null +++ b/Source/OpenTK/NativeWindow.cs @@ -0,0 +1,900 @@ +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 NativeWindow0 : INativeWindow + { + #region --- Fields --- + + private bool disposed; + + private readonly INativeWindow implementation; + + #endregion + + #region --- Contructors --- + + #region NativeWindow() + + /// Constructs a new NativeWindow with default attributes without enabling events. + public NativeWindow0() + : this(640, 480, "OpenTK Native Window", GameWindowFlags.Default, GraphicsMode.Default, DisplayDevice.Default, false) { } + + #endregion + + #region + + /// Constructs a new NativeWindow with default attributes. + /// Indicates to enable event processing as part of the NativeWindow construction. + public NativeWindow0(bool enableEvents) + : this(640, 480, "OpenTK Native Window", GameWindowFlags.Default, GraphicsMode.Default, DisplayDevice.Default, enableEvents) { } + + #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 without enabling events. + /// 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 NativeWindow0(int width, int height, string title, GameWindowFlags options, GraphicsMode mode, DisplayDevice device) + : this(width, height, title, options, mode, device, false) { } + + #endregion + + #region NativeWindow(int width, int height, string title, GameWindowFlags options, GraphicsMode mode, DisplayDevice device, bool enableEvents) + + /// 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. + /// Indicates to enable event processing as part of the NativeWindow construction. + /// If width or height is less than 1. + /// If mode or device is null. + public NativeWindow0(int width, int height, string title, GameWindowFlags options, GraphicsMode mode, DisplayDevice device, bool enableEvents) + : this(device.Bounds.Left + (device.Bounds.Width - width) / 2, + device.Bounds.Top + (device.Bounds.Height - height) / 2, + width, height, title, options, mode, device, enableEvents) { } + + #endregion + + #region NativeWindow0(int x, int y, int width, int height, string title, GameWindowFlags options, GraphicsMode mode, DisplayDevice device) + + /// Constructs a new NativeWindow with the specified attributes without enabling events. + /// 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 NativeWindow0(int x, int y, int width, int height, string title, GameWindowFlags options, GraphicsMode mode, DisplayDevice device) + : this(x, y, width, height, title, options, mode, device, false) { } + + #endregion + + #region NativeWindow0(int x, int y, int width, int height, string title, GameWindowFlags options, GraphicsMode mode, DisplayDevice device, bool enableEvents) + + /// 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. + /// Indicates to enable event processing as part of the NativeWindow construction. + /// If width or height is less than 1. + /// If mode or device is null. + public NativeWindow0(int x, int y, int width, int height, string title, GameWindowFlags options, GraphicsMode mode, DisplayDevice device, bool enableEvents) + { + // 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"); + + implementation = Factory.Default.CreateNativeWindow(x, y, width, height, title, mode, options, device); + + if ((options & GameWindowFlags.Fullscreen) != 0) + { + device.ChangeResolution(width, height, mode.ColorFormat.BitsPerPixel, 0); + WindowState = WindowState.Fullscreen; + } + + if (enableEvents) EnableEvents(); + } + + #endregion + + #endregion + + #region --- INativeWindow Members --- + + #region Methods + + #region Close + + /// + /// Closes the NativeWindow. Equivalent to calling . + /// + public void Close() + { + Dispose(); + } + + #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() + { + EnsureUndisposed(); + implementation.ProcessEvents(); + } + + #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 void Dispose() + { + implementation.Dispose(); + disposed = true; + GC.SuppressFinalize(this); + } + + #endregion + + #endregion + + #region --- Protected Members --- + + #region Methods + + #region DisableEvents + + /// + /// Disables the propagation of OS events. + /// + protected void DisableEvents() + { + EnsureUndisposed(); + 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; + } + + #endregion + + #region EnableEvents + + /// + /// Enables the propagation of OS events. + /// + protected void EnableEvents() + { + EnsureUndisposed(); + 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; + } + + #endregion + + #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) { } + + #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) { } + + #endregion + + #region OnMove + + /// + /// Called when the NativeWindow is moved. + /// + /// Not used. + protected virtual void OnMove(EventArgs e) { } + + #endregion + + #region OnResize + + /// + /// Called when the NativeWindow is resized. + /// + /// Not used. + protected virtual void OnResize(EventArgs e) { } + + #endregion + + #region OnWindowBorderChanged + + /// + /// Called when the WindowBorder of this NativeWindow has changed. + /// + /// Not used. + protected virtual void OnWindowBorderChanged(EventArgs e) { } + + #endregion + + #region OnWindowStateChanged + + /// + /// Called when the WindowState of this NativeWindow has changed. + /// + /// Not used. + protected virtual void OnWindowStateChanged(EventArgs e) { } + + #endregion + + #endregion + + #endregion + + #region --- Private Members --- + + #region OnClosedInternal + + private void OnClosedInternal(object sender, EventArgs e) + { + OnClosed(e); + + if (Closed != null) Closed(this, e); + } + + #endregion + + #region OnClosingInternal + + private void OnClosingInternal(object sender, CancelEventArgs e) + { + OnClosing(e); + + if (Closing != null) Closing(this, e); + + if (!e.Cancel) Close(); + } + + #endregion + + #region OnDisposedInternal + + private void OnDisposedInternal(object sender, EventArgs e) + { + // TODO: OnDisposed? + if (Disposed != null) Disposed(this, e); + } + + #endregion + + #region OnFocusedChangedInternal + + private void OnFocusedChangedInternal(object sender, EventArgs e) + { + // TODO: OnFocusedChanged? + if (FocusedChanged != null) FocusedChanged(this, e); + } + + #endregion + + #region OnKeyPressInternal + + private void OnKeyPressInternal(object sender, KeyPressEventArgs e) + { + // TODO: OnKeyPress? + if (KeyPress != null) KeyPress(this, e); + } + + #endregion + + #region OnMoveInternal + + private void OnMoveInternal(object sender, EventArgs e) + { + EnsureUndisposed(); + + //if (!this.Exists || this.IsExiting) return; // TODO: See EnableEvents and DisableEvents. + + OnMove(e); + + if (Move != null) Move(this, e); + } + + #endregion + + #region OnResizeInternal + + private void OnResizeInternal(object sender, EventArgs e) + { + EnsureUndisposed(); + + //if (!this.Exists || this.IsExiting) return; // TODO: See EnableEvents and DisableEvents. + + OnResize(e); + + if (Resize != null) Resize(this, e); + } + + #endregion + + #region OnTitleChangedInternal + + private void OnTitleChangedInternal(object sender, EventArgs e) + { + // TODO: OnTitleChanged? + if (TitleChanged != null) TitleChanged(this, e); + } + + #endregion + + #region OnVisibleChangedInternal + + private void OnVisibleChangedInternal(object sender, EventArgs e) + { + // TODO: OnVisibleChanged? + if (VisibleChanged != null) VisibleChanged(this, e); + } + + #endregion + + #region OnWindowBorderChangedInternal + + private void OnWindowBorderChangedInternal(object sender, EventArgs e) + { + OnWindowBorderChanged(e); + + if (WindowBorderChanged != null) WindowBorderChanged(this, e); // TODO: This was raised with EventArgs.Empty. Special reason? + } + + #endregion + + #region OnWindowStateChangedInternal + + private void OnWindowStateChangedInternal(object sender, EventArgs e) + { + OnWindowStateChanged(e); + + if (WindowStateChanged != null) WindowStateChanged(this, e); + } + + #endregion + + #endregion + } + +}