#region --- License --- /* Licensed under the MIT/X11 license. * Copyright (c) 2009 Novell, Inc. * Copyright 2013 Xamarin Inc * This notice may not be removed from any source distribution. * See license.txt for licensing detailed licensing details. */ // Copyright 2011 Xamarin Inc. All rights reserved. #endregion using System; using System.ComponentModel; using System.Drawing; using CoreAnimation; using CoreGraphics; using Foundation; using OpenGLES; using UIKit; using ObjCRuntime; using OpenTK; using OpenTK.Graphics; using OpenTK.Input; using OpenTK.Platform; using OpenTK.Platform.iPhoneOS; using All = OpenTK.Graphics.ES11.All; using ES11 = OpenTK.Graphics.ES11; using ES20 = OpenTK.Graphics.ES20; using ES30 = OpenTK.Graphics.ES30; namespace OpenTK.Platform.iPhoneOS { sealed class GLCalls { public delegate void glBindFramebuffer(All target, int framebuffer); public delegate void glBindRenderbuffer(All target, int renderbuffer); public delegate void glDeleteFramebuffers(int n, ref int framebuffers); public delegate void glDeleteRenderbuffers(int n, ref int renderbuffers); public delegate void glFramebufferRenderbuffer(All target, All attachment, All renderbuffertarget, int renderbuffer); public delegate void glGenFramebuffers(int n, out int framebuffers); public delegate void glGenRenderbuffers(int n, out int renderbuffers); public delegate void glGetInteger(All name, out int value); public delegate void glScissor(int x, int y, int width, int height); public delegate void glViewport(int x, int y, int width, int height); public delegate void glGetRenderBufferParameter(All target, All pname, out int p); public delegate void glPixelStore(All pname, int param); public delegate void glReadPixels(int x, int y, int width, int height, All format, All type, byte[] pixels); public glBindFramebuffer BindFramebuffer; public glBindRenderbuffer BindRenderbuffer; public glDeleteFramebuffers DeleteFramebuffers; public glDeleteRenderbuffers DeleteRenderbuffers; public glFramebufferRenderbuffer FramebufferRenderbuffer; public glGenFramebuffers GenFramebuffers; public glGenRenderbuffers GenRenderbuffers; public glGetInteger GetInteger; public glScissor Scissor; public glViewport Viewport; public glGetRenderBufferParameter GetRenderbufferParameter; public glPixelStore PixelStore; public glReadPixels ReadPixels; public static GLCalls GetGLCalls(EAGLRenderingAPI api) { switch (api) { case EAGLRenderingAPI.OpenGLES1: return CreateES1(); case EAGLRenderingAPI.OpenGLES2: return CreateES2(); case EAGLRenderingAPI.OpenGLES3: return CreateES3(); } throw new ArgumentException("api"); } static GLCalls CreateES1() { return new GLCalls() { BindFramebuffer = (t, f) => ES11.GL.Oes.BindFramebuffer(t, f), BindRenderbuffer = (t, r) => ES11.GL.Oes.BindRenderbuffer(t, r), DeleteFramebuffers = (int n, ref int f) => ES11.GL.Oes.DeleteFramebuffers(n, ref f), DeleteRenderbuffers = (int n, ref int r) => ES11.GL.Oes.DeleteRenderbuffers(n, ref r), FramebufferRenderbuffer = (t, a, rt, rb) => ES11.GL.Oes.FramebufferRenderbuffer(t, a, rt, rb), GenFramebuffers = (int n, out int f) => ES11.GL.Oes.GenFramebuffers(n, out f), GenRenderbuffers = (int n, out int r) => ES11.GL.Oes.GenRenderbuffers(n, out r), GetInteger = (All n, out int v) => ES11.GL.GetInteger(n, out v), Scissor = (x, y, w, h) => ES11.GL.Scissor(x, y, w, h), Viewport = (x, y, w, h) => ES11.GL.Viewport(x, y, w, h), GetRenderbufferParameter= (All t, All p, out int a) => ES11.GL.Oes.GetRenderbufferParameter (t, p, out a), PixelStore = (n, p) => ES11.GL.PixelStore(n, p), ReadPixels = (x, y, w, h, f, t, d) => ES11.GL.ReadPixels(x, y, w, h, f, t, d), }; } static GLCalls CreateES2() { return new GLCalls() { BindFramebuffer = (t, f) => ES20.GL.BindFramebuffer((ES20.FramebufferTarget) t, f), BindRenderbuffer = (t, r) => ES20.GL.BindRenderbuffer((ES20.RenderbufferTarget) t, r), DeleteFramebuffers = (int n, ref int f) => ES20.GL.DeleteFramebuffers(n, ref f), DeleteRenderbuffers = (int n, ref int r) => ES20.GL.DeleteRenderbuffers(n, ref r), FramebufferRenderbuffer = (t, a, rt, rb) => ES20.GL.FramebufferRenderbuffer((ES20.FramebufferTarget) t, (ES20.FramebufferSlot) a, (ES20.RenderbufferTarget) rt, rb), GenFramebuffers = (int n, out int f) => ES20.GL.GenFramebuffers(n, out f), GenRenderbuffers = (int n, out int r) => ES20.GL.GenRenderbuffers(n, out r), GetInteger = (All n, out int v) => ES20.GL.GetInteger((ES20.GetPName) n, out v), Scissor = (x, y, w, h) => ES20.GL.Scissor(x, y, w, h), Viewport = (x, y, w, h) => ES20.GL.Viewport(x, y, w, h), GetRenderbufferParameter= (All t, All p, out int a) => ES20.GL.GetRenderbufferParameter ((ES20.RenderbufferTarget) t, (ES20.RenderbufferParameterName) p, out a), PixelStore = (n, p) => ES20.GL.PixelStore((ES20.PixelStoreParameter) n, p), ReadPixels = (x, y, w, h, f, t, d) => ES20.GL.ReadPixels(x, y, w, h, (ES20.PixelFormat) f, (ES20.PixelType) t, d), }; } static GLCalls CreateES3() { return new GLCalls() { BindFramebuffer = (t, f) => ES30.GL.BindFramebuffer((ES30.FramebufferTarget) t, f), BindRenderbuffer = (t, r) => ES30.GL.BindRenderbuffer((ES30.RenderbufferTarget) t, r), DeleteFramebuffers = (int n, ref int f) => ES30.GL.DeleteFramebuffers(n, ref f), DeleteRenderbuffers = (int n, ref int r) => ES30.GL.DeleteRenderbuffers(n, ref r), FramebufferRenderbuffer = (t, a, rt, rb) => ES30.GL.FramebufferRenderbuffer((ES30.FramebufferTarget) t, (ES30.FramebufferAttachment) a, (ES30.RenderbufferTarget) rt, rb), GenFramebuffers = (int n, out int f) => ES30.GL.GenFramebuffers(n, out f), GenRenderbuffers = (int n, out int r) => ES30.GL.GenRenderbuffers(n, out r), GetInteger = (All n, out int v) => ES30.GL.GetInteger((ES30.GetPName) n, out v), Scissor = (x, y, w, h) => ES30.GL.Scissor(x, y, w, h), Viewport = (x, y, w, h) => ES30.GL.Viewport(x, y, w, h), GetRenderbufferParameter= (All t, All p, out int a) => ES30.GL.GetRenderbufferParameter ((ES30.RenderbufferTarget) t, (ES30.RenderbufferParameterName) p, out a), PixelStore = (n, p) => ES30.GL.PixelStore((ES30.PixelStoreParameter) n, p), ReadPixels = (x, y, w, h, f, t, d) => ES30.GL.ReadPixels(x, y, w, h, (ES30.PixelFormat) f, (ES30.PixelType) t, d), }; } } interface ITimeSource { void Suspend (); void Resume (); void Invalidate (); } [Register] class CADisplayLinkTimeSource : NSObject, ITimeSource { const string selectorName = "runIteration:"; static Selector selRunIteration = new Selector (selectorName); iPhoneOSGameView view; CADisplayLink displayLink; public CADisplayLinkTimeSource (iPhoneOSGameView view, int frameInterval) { this.view = view; if (displayLink != null) displayLink.Invalidate (); displayLink = CADisplayLink.Create (this, selRunIteration); displayLink.FrameInterval = frameInterval; displayLink.Paused = true; } public void Suspend () { displayLink.Paused = true; } public void Resume () { displayLink.Paused = false; displayLink.AddToRunLoop (NSRunLoop.Main, NSRunLoop.NSDefaultRunLoopMode); } public void Invalidate () { if (displayLink != null) { displayLink.Invalidate (); displayLink = null; } } [Export (selectorName)] [Preserve (Conditional = true)] void RunIteration (NSObject parameter) { view.RunIteration (null); } } class NSTimerTimeSource : ITimeSource { TimeSpan timeout; NSTimer timer; iPhoneOSGameView view; public NSTimerTimeSource (iPhoneOSGameView view, double updatesPerSecond) { this.view = view; // Can't use TimeSpan.FromSeconds() as that only has 1ms // resolution, and we need better (e.g. 60fps doesn't fit nicely // in 1ms resolution, but does in ticks). timeout = new TimeSpan ((long) (((1.0 * TimeSpan.TicksPerSecond) / updatesPerSecond) + 0.5)); } public void Suspend () { if (timer != null) { timer.Invalidate (); timer = null; } } public void Resume () { if (timeout != new TimeSpan (-1)) timer = NSTimer.CreateRepeatingScheduledTimer (timeout, view.RunIteration); } public void Invalidate () { if (timer != null) { timer.Invalidate (); timer = null; } } } public class iPhoneOSGameView : UIView, IGameWindow { bool suspended; bool disposed; int framebuffer, renderbuffer; GLCalls gl; ITimeSource timesource; System.Diagnostics.Stopwatch stopwatch; TimeSpan prevUpdateTime; TimeSpan prevRenderTime; IWindowInfo windowInfo = Utilities.CreateDummyWindowInfo(); [Export("initWithCoder:")] public iPhoneOSGameView(NSCoder coder) : base(coder) { stopwatch = new System.Diagnostics.Stopwatch (); } [Export("initWithFrame:")] public iPhoneOSGameView(System.Drawing.RectangleF frame) : base(frame) { stopwatch = new System.Diagnostics.Stopwatch (); } [Export ("layerClass")] public static Class GetLayerClass () { return new Class (typeof (CAEAGLLayer)); } void AssertValid() { if (disposed) throw new ObjectDisposedException(""); } void AssertContext() { if (GraphicsContext == null) throw new InvalidOperationException("Operation requires a GraphicsContext, which hasn't been created yet."); } EAGLRenderingAPI api; public EAGLRenderingAPI ContextRenderingApi { get { AssertValid(); return api; } set { AssertValid(); if (GraphicsContext != null) throw new NotSupportedException("Can't change RenderingApi after GraphicsContext is constructed."); this.api = value; } } public IGraphicsContext GraphicsContext {get; set;} public EAGLContext EAGLContext { get { AssertValid(); if (GraphicsContext != null) { iPhoneOSGraphicsContext c = GraphicsContext as iPhoneOSGraphicsContext; if (c != null) return c.EAGLContext; var i = GraphicsContext as IGraphicsContextInternal; IGraphicsContext ic = i == null ? null : i.Implementation; c = ic as iPhoneOSGraphicsContext; if (c != null) return c.EAGLContext; } return null; } } bool retainedBacking; public bool LayerRetainsBacking { get { AssertValid(); return retainedBacking; } set { AssertValid(); if (GraphicsContext != null) throw new NotSupportedException("Can't change LayerRetainsBacking after GraphicsContext is constructed."); retainedBacking = value; } } NSString colorFormat; public NSString LayerColorFormat { get { AssertValid(); return colorFormat; } set { AssertValid(); if (GraphicsContext != null) throw new NotSupportedException("Can't change LayerColorFormat after GraphicsContext is constructed."); colorFormat = value; } } public int Framebuffer { get {return framebuffer;} } public int Renderbuffer { get {return renderbuffer;} } public bool AutoResize {get; set;} UIViewController GetViewController() { UIResponder r = this; while (r != null) { var c = r as UIViewController; if (c != null) return c; r = r.NextResponder; } return null; } public virtual string Title { get { AssertValid(); var c = GetViewController(); if (c != null) return c.Title; throw new NotSupportedException(); } set { AssertValid(); var c = GetViewController(); if (c != null) { if (c.Title != value) { c.Title = value; OnTitleChanged(EventArgs.Empty); } } else throw new NotSupportedException(); } } protected virtual void OnTitleChanged(EventArgs e) { var h = TitleChanged; if (h != null) h (this, EventArgs.Empty); } bool INativeWindow.Focused { get {throw new NotImplementedException();} } public virtual bool Visible { get { AssertValid(); return !base.Hidden; } set { AssertValid(); if (base.Hidden != !value) { base.Hidden = !value; OnVisibleChanged(EventArgs.Empty); } } } protected virtual void OnVisibleChanged(EventArgs e) { var h = VisibleChanged; if (h != null) h (this, EventArgs.Empty); } bool INativeWindow.Exists { get {throw new NotImplementedException();} } public virtual IWindowInfo WindowInfo { get { AssertValid(); return windowInfo; } } public virtual WindowState WindowState { get { AssertValid(); var c = GetViewController(); #if TVOS if (c != null && (c.EdgesForExtendedLayout == UIRectEdge.None)) #else if (c != null && c.WantsFullScreenLayout) #endif return WindowState.Fullscreen; return WindowState.Normal; } set { AssertValid(); var c = GetViewController(); if (c != null) { var fullscreen = (value == WindowState.Fullscreen); #if TVOS if ((c.EdgesForExtendedLayout == UIRectEdge.None) == fullscreen) { c.EdgesForExtendedLayout = fullscreen ? UIRectEdge.None : UIRectEdge.All; OnWindowStateChanged(EventArgs.Empty); } #else if (c.WantsFullScreenLayout == fullscreen) { c.WantsFullScreenLayout = fullscreen; OnWindowStateChanged(EventArgs.Empty); } #endif } } } protected virtual void OnWindowStateChanged(EventArgs e) { var h = WindowStateChanged; if (h != null) h (this, EventArgs.Empty); } public virtual WindowBorder WindowBorder { get { AssertValid(); return WindowBorder.Hidden; } set {} } Rectangle INativeWindow.Bounds { get {throw new NotSupportedException();} set {throw new NotSupportedException();} } Point INativeWindow.Location { get {throw new NotSupportedException();} set {throw new NotSupportedException();} } Size size; public Size Size { get { AssertValid(); return size; } set { AssertValid(); if (size != value) { size = value; OnResize(EventArgs.Empty); } } } protected virtual void OnResize(EventArgs e) { var h = Resize; if (h != null) h (this, e); } int INativeWindow.X { get {throw new NotSupportedException();} set {throw new NotSupportedException();} } int INativeWindow.Y { get {throw new NotSupportedException();} set {throw new NotSupportedException();} } int INativeWindow.Width { get {throw new NotSupportedException();} set {throw new NotSupportedException();} } int INativeWindow.Height { get {throw new NotSupportedException();} set {throw new NotSupportedException();} } Rectangle INativeWindow.ClientRectangle { get {throw new NotSupportedException();} set {throw new NotSupportedException();} } Size INativeWindow.ClientSize { get {throw new NotSupportedException();} set {throw new NotSupportedException();} } protected virtual void CreateFrameBuffer() { AssertValid(); if (LayerColorFormat == null) throw new InvalidOperationException("Set the LayerColorFormat property to an EAGLColorFormat value before calling Run()."); CAEAGLLayer eaglLayer = (CAEAGLLayer) Layer; eaglLayer.DrawableProperties = NSDictionary.FromObjectsAndKeys ( new NSObject [] {NSNumber.FromBoolean(LayerRetainsBacking), LayerColorFormat}, new NSObject [] {EAGLDrawableProperty.RetainedBacking, EAGLDrawableProperty.ColorFormat} ); ConfigureLayer(eaglLayer); int major = 0, minor = 0; switch (ContextRenderingApi) { case EAGLRenderingAPI.OpenGLES1: major = 1; minor = 1; break; case EAGLRenderingAPI.OpenGLES2: major = 2; minor = 0; break; case EAGLRenderingAPI.OpenGLES3: major = 3; minor = 0; break; default: throw new ArgumentException("Unsupported EAGLRenderingAPI version: " + ContextRenderingApi); } GraphicsContext = new GraphicsContext(GraphicsMode.Default, WindowInfo, major, minor, GraphicsContextFlags.Embedded); GraphicsContext.MakeCurrent(WindowInfo); gl = GLCalls.GetGLCalls(ContextRenderingApi); int oldFramebuffer = 0, oldRenderbuffer = 1; gl.GetInteger(All.FramebufferBindingOes, out oldFramebuffer); gl.GetInteger(All.RenderbufferBindingOes, out oldRenderbuffer); gl.GenRenderbuffers(1, out renderbuffer); gl.BindRenderbuffer(All.RenderbufferOes, renderbuffer); if (!EAGLContext.RenderBufferStorage((uint) All.RenderbufferOes, eaglLayer)) { gl.DeleteRenderbuffers(1, ref renderbuffer); renderbuffer = 0; gl.BindRenderbuffer(All.RenderbufferBindingOes, oldRenderbuffer); throw new InvalidOperationException("Error with EAGLContext.RenderBufferStorage!"); } gl.GenFramebuffers (1, out framebuffer); gl.BindFramebuffer (All.FramebufferOes, framebuffer); gl.FramebufferRenderbuffer (All.FramebufferOes, All.ColorAttachment0Oes, All.RenderbufferOes, renderbuffer); Size newSize = new Size( (int) Math.Round(eaglLayer.Bounds.Size.Width), (int) Math.Round(eaglLayer.Bounds.Size.Height)); Size = newSize; gl.Viewport(0, 0, newSize.Width, newSize.Height); gl.Scissor(0, 0, newSize.Width, newSize.Height); frameBufferWindow = new WeakReference(Window); frameBufferLayer = new WeakReference(Layer); } protected virtual void ConfigureLayer(CAEAGLLayer eaglLayer) { } protected virtual void DestroyFrameBuffer() { AssertValid(); AssertContext(); EAGLContext oldContext = EAGLContext.CurrentContext; if (!GraphicsContext.IsCurrent) MakeCurrent(); gl.DeleteFramebuffers (1, ref framebuffer); gl.DeleteRenderbuffers (1, ref renderbuffer); framebuffer = renderbuffer = 0; if (oldContext != EAGLContext) EAGLContext.SetCurrentContext(oldContext); else EAGLContext.SetCurrentContext(null); GraphicsContext.Dispose(); GraphicsContext = null; gl = null; } public virtual void Close() { AssertValid(); OnClosed(EventArgs.Empty); } protected virtual void OnClosed(EventArgs e) { var h = Closed; if (h != null) h (this, e); } protected override void Dispose(bool disposing) { if (disposed) return; if (disposing) { if (timesource != null) timesource.Invalidate (); timesource = null; if (stopwatch != null) stopwatch.Stop(); stopwatch = null; DestroyFrameBuffer(); } base.Dispose (disposing); disposed = true; if (disposing) OnDisposed(EventArgs.Empty); } protected virtual void OnDisposed(EventArgs e) { var h = Disposed; if (h != null) h (this, e); } void INativeWindow.ProcessEvents() { throw new NotSupportedException(); } Point INativeWindow.PointToClient(Point point) { return point; } Point INativeWindow.PointToScreen(Point point) { return point; } public override void LayoutSubviews() { base.LayoutSubviews (); if (GraphicsContext == null) return; var bounds = Bounds; if (AutoResize && (Math.Round(bounds.Width) != Size.Width || Math.Round(bounds.Height) != Size.Height)) { DestroyFrameBuffer(); CreateFrameBuffer(); } } public virtual void MakeCurrent() { AssertValid(); AssertContext(); GraphicsContext.MakeCurrent(WindowInfo); } public virtual void SwapBuffers() { AssertValid(); AssertContext(); gl.BindRenderbuffer(All.RenderbufferOes, renderbuffer); GraphicsContext.SwapBuffers(); } WeakReference frameBufferWindow; WeakReference frameBufferLayer; public void Run() { RunWithFrameInterval (1); } public void Run (double updatesPerSecond) { if (updatesPerSecond < 0.0) throw new ArgumentException ("updatesPerSecond"); if (updatesPerSecond == 0.0) { RunWithFrameInterval (1); return; } if (timesource != null) timesource.Invalidate (); timesource = new NSTimerTimeSource (this, updatesPerSecond); CreateFrameBuffer (); OnLoad (EventArgs.Empty); Start (); } [Obsolete ("Use either Run (float updatesPerSecond) or RunWithFrameInterval (int frameInterval)")] public void Run (int frameInterval) { RunWithFrameInterval (frameInterval); } public void RunWithFrameInterval (int frameInterval) { AssertValid (); if (frameInterval < 1) throw new ArgumentException ("frameInterval"); if (timesource != null) timesource.Invalidate (); timesource = new CADisplayLinkTimeSource (this, frameInterval); CreateFrameBuffer (); OnLoad (EventArgs.Empty); Start (); } void Start () { prevUpdateTime = TimeSpan.Zero; prevRenderTime = TimeSpan.Zero; Resume (); } public void Stop() { AssertValid(); if (timesource != null) { timesource.Invalidate (); timesource = null; } suspended = false; OnUnload(EventArgs.Empty); } void Suspend () { if (timesource != null) timesource.Suspend (); stopwatch.Stop(); suspended = true; } void Resume () { if (timesource != null) timesource.Resume (); stopwatch.Start(); suspended = false; } public UIImage Capture () { // This is from: https://developer.apple.com/library/ios/#qa/qa2010/qa1704.html int backingWidth, backingHeight; // Bind the color renderbuffer used to render the OpenGL ES view // If your application only creates a single color renderbuffer which is already bound at this point, // this call is redundant, but it is needed if you're dealing with multiple renderbuffers. // Note, replace "_colorRenderbuffer" with the actual name of the renderbuffer object defined in your class. gl.BindRenderbuffer (All.RenderbufferOes, Renderbuffer); // Get the size of the backing CAEAGLLayer gl.GetRenderbufferParameter (All.RenderbufferOes, All.RenderbufferWidthOes, out backingWidth); gl.GetRenderbufferParameter (All.RenderbufferOes, All.RenderbufferHeightOes, out backingHeight); int width = backingWidth, height = backingHeight; int dataLength = width * height * 4; byte[] data = new byte [dataLength]; // Read pixel data from the framebuffer gl.PixelStore (All.PackAlignment, 4); gl.ReadPixels (0, 0, width, height, All.Rgba, All.UnsignedByte, data); // Create a CGImage with the pixel data // If your OpenGL ES content is opaque, use kCGImageAlphaNoneSkipLast to ignore the alpha channel // otherwise, use kCGImageAlphaPremultipliedLast using (var data_provider = new CGDataProvider (data, 0, data.Length)) { using (var colorspace = CGColorSpace.CreateDeviceRGB ()) { using (var iref = new CGImage (width, height, 8, 32, width * 4, colorspace, (CGImageAlphaInfo) ((int) CGBitmapFlags.ByteOrder32Big | (int) CGImageAlphaInfo.PremultipliedLast), data_provider, null, true, CGColorRenderingIntent.Default)) { // OpenGL ES measures data in PIXELS // Create a graphics context with the target size measured in POINTS int widthInPoints, heightInPoints; float scale = (float) ContentScaleFactor; widthInPoints = (int) (width / scale); heightInPoints = (int) (height / scale); UIGraphics.BeginImageContextWithOptions (new System.Drawing.SizeF (widthInPoints, heightInPoints), false, scale); try { var cgcontext = UIGraphics.GetCurrentContext (); // UIKit coordinate system is upside down to GL/Quartz coordinate system // Flip the CGImage by rendering it to the flipped bitmap context // The size of the destination area is measured in POINTS cgcontext.SetBlendMode (CGBlendMode.Copy); cgcontext.DrawImage (new System.Drawing.RectangleF (0, 0, widthInPoints, heightInPoints), iref); // Retrieve the UIImage from the current context var image = UIGraphics.GetImageFromCurrentImageContext (); return image; } finally { UIGraphics.EndImageContext (); } } } } } public override void WillMoveToWindow(UIWindow window) { if (window == null && !suspended) Suspend(); else if (window != null && suspended) { if (frameBufferLayer != null && ((CALayer)frameBufferLayer.Target) != Layer || frameBufferWindow != null && ((UIWindow)frameBufferWindow.Target) != window) { DestroyFrameBuffer(); CreateFrameBuffer (); } Resume (); } } FrameEventArgs updateEventArgs = new FrameEventArgs(); FrameEventArgs renderEventArgs = new FrameEventArgs(); internal void RunIteration (NSTimer timer) { var curUpdateTime = stopwatch.Elapsed; if (prevUpdateTime == TimeSpan.Zero) prevUpdateTime = curUpdateTime; var t = (curUpdateTime - prevUpdateTime).TotalSeconds; updateEventArgs.Time = t; OnUpdateFrame(updateEventArgs); prevUpdateTime = curUpdateTime; gl.BindFramebuffer(All.FramebufferOes, framebuffer); var curRenderTime = stopwatch.Elapsed; if (prevRenderTime == TimeSpan.Zero) prevRenderTime = curRenderTime; t = (curRenderTime - prevRenderTime).TotalSeconds; renderEventArgs.Time = t; OnRenderFrame(renderEventArgs); prevRenderTime = curRenderTime; } protected virtual void OnLoad(EventArgs e) { var h = Load; if (h != null) h (this, e); } protected virtual void OnUnload(EventArgs e) { var h = Unload; DestroyFrameBuffer(); if (h != null) h (this, e); } protected virtual void OnUpdateFrame(FrameEventArgs e) { var h = UpdateFrame; if (h != null) h (this, e); } protected virtual void OnRenderFrame(FrameEventArgs e) { var h = RenderFrame; if (h != null) h (this, e); } event EventHandler INativeWindow.Move { add {throw new NotSupportedException();} remove {throw new NotSupportedException();} } public event EventHandler Resize; event EventHandler INativeWindow.Closing { add {throw new NotSupportedException();} remove {throw new NotSupportedException();} } public event EventHandler Closed; public event EventHandler Disposed; public event EventHandler TitleChanged; public event EventHandler VisibleChanged; event EventHandler INativeWindow.FocusedChanged { add {throw new NotSupportedException();} remove {throw new NotSupportedException();} } event EventHandler INativeWindow.WindowBorderChanged { add {throw new NotSupportedException();} remove {throw new NotSupportedException();} } public event EventHandler WindowStateChanged; event EventHandler INativeWindow.KeyPress { add {throw new NotSupportedException();} remove {throw new NotSupportedException();} } public event EventHandler Load; public event EventHandler Unload; public event EventHandler UpdateFrame; public event EventHandler RenderFrame; public OpenTK.Input.IInputDriver InputDriver { get { throw new NotSupportedException(); } } MouseCursor INativeWindow.Cursor { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } } bool INativeWindow.CursorVisible { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } } Icon INativeWindow.Icon { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } } event EventHandler INativeWindow.IconChanged { add { throw new NotSupportedException(); } remove { throw new NotSupportedException(); } } event EventHandler INativeWindow.KeyDown { add { throw new NotSupportedException(); } remove { throw new NotSupportedException(); } } event EventHandler INativeWindow.KeyUp { add { throw new NotSupportedException(); } remove { throw new NotSupportedException(); } } event EventHandler INativeWindow.MouseDown { add { throw new NotSupportedException(); } remove { throw new NotSupportedException(); } } event EventHandler INativeWindow.MouseMove { add { throw new NotSupportedException(); } remove { throw new NotSupportedException(); } } event EventHandler INativeWindow.MouseUp { add { throw new NotSupportedException(); } remove { throw new NotSupportedException(); } } event EventHandler INativeWindow.MouseWheel { add { throw new NotSupportedException(); } remove { throw new NotSupportedException(); } } public event EventHandler MouseEnter { add { throw new NotSupportedException(); } remove { throw new NotSupportedException(); } } public event EventHandler MouseLeave { add { throw new NotSupportedException(); } remove { throw new NotSupportedException(); } } } } // vim: et ts=4 sw=4