#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
}
}