#region License // // The Open Toolkit Library License // // Copyright (c) 2006 - 2010 the Open Toolkit library. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // #endregion // Created by Erik Ylvisaker on 3/17/08. using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; namespace OpenTK.Platform.MacOS { using Carbon; using Graphics; using AGLRendererInfo = IntPtr; using AGLPixelFormat = IntPtr; using AGLContext = IntPtr; using AGLPbuffer = IntPtr; /// /// AGL context implementation for WinForms compatibility. /// class AglContext : IGraphicsContext, IGraphicsContextInternal { ContextHandle Handle; GraphicsMode mode; IWindowInfo carbonWindow; IGraphicsContext dummyContext; // for extension loading bool disposed; bool error_checking; readonly GetInt XOffset; readonly GetInt YOffset; public AglContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shareContext, GetInt xoffset, GetInt yoffset) { Debug.Print("Share context: {0}", shareContext); Debug.Print("Window info: {0}", window); IntPtr shareContextRef = IntPtr.Zero; XOffset = xoffset; YOffset = yoffset; carbonWindow = window; if (shareContext is AglContext) { shareContextRef = ((AglContext)shareContext).Handle.Handle; } else if (shareContext is GraphicsContext) { ContextHandle shareHandle = shareContext != null ? (shareContext as IGraphicsContextInternal).Context : (ContextHandle)IntPtr.Zero; shareContextRef = shareHandle.Handle; } if (shareContextRef == IntPtr.Zero) { Debug.Print("No context sharing will take place."); } CreateContext(mode, carbonWindow, shareContextRef, true); } private void AddPixelAttrib(List aglAttributes, Agl.PixelFormatAttribute pixelFormatAttribute) { Debug.Print(pixelFormatAttribute.ToString()); aglAttributes.Add((int)pixelFormatAttribute); } private void AddPixelAttrib(List aglAttributes, Agl.PixelFormatAttribute pixelFormatAttribute, int value) { Debug.Print("{0} : {1}", pixelFormatAttribute, value); aglAttributes.Add((int)pixelFormatAttribute); aglAttributes.Add(value); } void CreateContext(GraphicsMode mode, IWindowInfo carbonWindow, IntPtr shareContextRef, bool fullscreen) { Debug.Print("AGL pixel format attributes:"); // Choose a pixel format with the attributes we specified. AGLPixelFormat pixelformat; AglGraphicsMode selector = new AglGraphicsMode(); Mode = selector.SelectGraphicsMode( mode.ColorFormat, mode.Depth, mode.Stencil, mode.Samples, mode.AccumulatorFormat, mode.Buffers, mode.Stereo, out pixelformat); MyAGLReportError("aglChoosePixelFormat"); Debug.Print("Creating AGL context. Sharing with {0}", shareContextRef); // create the context and share it with the share reference. Handle = new ContextHandle(Agl.aglCreateContext(pixelformat, shareContextRef)); MyAGLReportError("aglCreateContext"); // Free the pixel format from memory. Agl.aglDestroyPixelFormat(pixelformat); MyAGLReportError("aglDestroyPixelFormat"); SetDrawable(carbonWindow); SetBufferRect(carbonWindow); Update(carbonWindow); MakeCurrent(carbonWindow); Debug.Print("context: {0}", Handle.Handle); dummyContext = new GraphicsContext(Handle, GetAddress, delegate() { return new ContextHandle(Agl.aglGetCurrentContext()); }); } void SetBufferRect(IWindowInfo carbonWindow) { Rect rect = API.GetControlBounds(carbonWindow.Handle); Debug.Print("Setting buffer_rect for control."); Debug.Print("MacOS Coordinate Rect: {0}", rect); int[] glrect = new int[4]; if (XOffset != null) glrect[0] = rect.X + XOffset(); else glrect[0] = rect.X; if (YOffset != null) glrect[1] = rect.Y + YOffset(); else glrect[1] = rect.Y; glrect[2] = rect.Width; glrect[3] = rect.Height; Agl.aglSetInteger(Handle.Handle, Agl.ParameterNames.AGL_BUFFER_RECT, glrect); MyAGLReportError("aglSetInteger"); Agl.aglEnable(Handle.Handle, Agl.ParameterNames.AGL_BUFFER_RECT); MyAGLReportError("aglEnable"); } void SetDrawable(IWindowInfo carbonWindow) { IntPtr windowPort = GetWindowPortForWindowInfo(carbonWindow); //Debug.Print("Setting drawable for context {0} to window port: {1}", Handle.Handle, windowPort); Agl.aglSetDrawable(Handle.Handle, windowPort); MyAGLReportError("aglSetDrawable"); } private static IntPtr GetWindowPortForWindowInfo(IWindowInfo carbonWindow) { IntPtr windowPort; IntPtr controlOwner = API.GetControlOwner(carbonWindow.Handle); windowPort = API.GetWindowPort(controlOwner); return windowPort; } public void Update(IWindowInfo window) { SetDrawable(window); SetBufferRect(window); Agl.aglUpdateContext(Handle.Handle); } void MyAGLReportError(string function) { Agl.AglError err = Agl.GetError(); if (err != Agl.AglError.NoError) throw new Exception(String.Format( "AGL Error from function {0}: {1} {2}", function, err, Agl.ErrorString(err))); } #region IGraphicsContext Members bool firstSwap = true; public void SwapBuffers() { // this is part of the hack to avoid dropping the first frame when // using multiple GLControls. if (firstSwap) { Debug.WriteLine("--> Resetting drawable. <--"); firstSwap = false; SetDrawable(carbonWindow); Update(carbonWindow); } Agl.aglSwapBuffers(Handle.Handle); MyAGLReportError("aglSwapBuffers"); } public void MakeCurrent(IWindowInfo window) { if (Agl.aglSetCurrentContext(Handle.Handle) == false) MyAGLReportError("aglSetCurrentContext"); } public bool IsCurrent { get { return (Handle.Handle == Agl.aglGetCurrentContext()); } } public int SwapInterval { get { int swap_interval = 0; if (Agl.aglGetInteger(Handle.Handle, Agl.ParameterNames.AGL_SWAP_INTERVAL, out swap_interval)) { return swap_interval; } else { MyAGLReportError("aglGetInteger"); return 0; } } set { if (!Agl.aglSetInteger(Handle.Handle, Agl.ParameterNames.AGL_SWAP_INTERVAL, ref value)) MyAGLReportError("aglSetInteger"); } } public GraphicsMode Mode { get { return mode; } set { mode = value; } } public void LoadAll() { dummyContext.LoadAll(); } public bool IsDisposed { get { return disposed; } } public bool VSync { get { return SwapInterval != 0; } set { SwapInterval = value ? 1 : 0; } } public GraphicsMode GraphicsMode { get { return mode; } } public bool ErrorChecking { get { return error_checking; } set { error_checking = value; } } #endregion #region IDisposable Members ~AglContext() { Dispose(false); } public void Dispose() { Dispose(true); } void Dispose(bool disposing) { if (IsDisposed || Handle.Handle == IntPtr.Zero) return; Debug.Print("Disposing of AGL context."); Agl.aglSetCurrentContext(IntPtr.Zero); //Debug.Print("Setting drawable to null for context {0}.", Handle.Handle); //Agl.aglSetDrawable(Handle.Handle, IntPtr.Zero); // I do not know MacOS allows us to destroy a context from a separate thread, // like the finalizer thread. It's untested, but worst case is probably // an exception on application exit, which would be logged to the console. // Actually, it seems to crash the mono runtime. -AMK 2013 Debug.Print("Destroying context"); if (Agl.aglDestroyContext(Handle.Handle) == true) { Debug.Print("Context destruction completed successfully."); Handle = ContextHandle.Zero; return; } // failed to destroy context. Debug.WriteLine("Failed to destroy context."); Debug.WriteLine(Agl.ErrorString(Agl.GetError())); // don't throw an exception from the finalizer thread. if (disposing) { throw new Exception(Agl.ErrorString(Agl.GetError())); } disposed = true; } #endregion #region IGraphicsContextInternal Members public IntPtr GetAddress(string function) { return NS.GetAddress(function); } public IntPtr GetAddress(IntPtr function) { return NS.GetAddress(function); } public IGraphicsContext Implementation { get { return this; } } public ContextHandle Context { get { return Handle; } } #endregion } }