#region --- License --- /* Copyright (c) 2006, 2007 Stefanos Apostolopoulos * See license.txt for license info */ #endregion #region --- Using Directives --- using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.Reflection; using System.Diagnostics; using OpenTK.Graphics; #endregion namespace OpenTK.Platform { namespace MacOS { /// /// This delegate represents any method that takes no arguments and returns an int. /// I would have used Func but that requires .NET 4 /// /// The int value that your method returns public delegate int GetInt(); } /// /// Provides cross-platform utilities to help interact with the underlying platform. /// public static class Utilities { #region internal static bool ThrowOnX11Error static bool throw_on_error; internal static bool ThrowOnX11Error { get { return throw_on_error; } set { if (value && !throw_on_error) { Type.GetType("System.Windows.Forms.XplatUIX11, System.Windows.Forms") .GetField("ErrorExceptions", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic) .SetValue(null, true); throw_on_error = true; } else if (!value && throw_on_error) { Type.GetType("System.Windows.Forms.XplatUIX11, System.Windows.Forms") .GetField("ErrorExceptions", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic) .SetValue(null, false); throw_on_error = false; } } } #endregion #region internal static void LoadExtensions(Type type) delegate Delegate LoadDelegateFunction(string name, Type signature); /// /// Loads all extensions for the specified class. This function is intended /// for OpenGL, Wgl, Glx, OpenAL etc. /// The class to load extensions for. /// /// The Type must contain a nested class called "Delegates". /// /// The Type must also implement a static function called LoadDelegate with the /// following signature: /// static Delegate LoadDelegate(string name, Type signature) /// /// This function allocates memory. /// internal static void LoadExtensions(Type type) { // Using reflection is more than 3 times faster than directly loading delegates on the first // run, probably due to code generation overhead. Subsequent runs are faster with direct loading // than with reflection, but the first time is more significant. int supported = 0; Type extensions_class = type.GetNestedType("Delegates", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); if (extensions_class == null) throw new InvalidOperationException("The specified type does not have any loadable extensions."); FieldInfo[] delegates = extensions_class.GetFields(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); if (delegates == null) throw new InvalidOperationException("The specified type does not have any loadable extensions."); MethodInfo load_delegate_method_info = type.GetMethod("LoadDelegate", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); if (load_delegate_method_info == null) throw new InvalidOperationException(type.ToString() + " does not contain a static LoadDelegate method."); LoadDelegateFunction LoadDelegate = (LoadDelegateFunction)Delegate.CreateDelegate( typeof(LoadDelegateFunction), load_delegate_method_info); Debug.Write("Load extensions for " + type.ToString() + "... "); System.Diagnostics.Stopwatch time = new System.Diagnostics.Stopwatch(); time.Reset(); time.Start(); foreach (FieldInfo f in delegates) { Delegate d = LoadDelegate(f.Name, f.FieldType); if (d != null) ++supported; f.SetValue(null, d); } FieldInfo rebuildExtensionList = type.GetField("rebuildExtensionList", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); if (rebuildExtensionList != null) rebuildExtensionList.SetValue(null, true); time.Stop(); Debug.Print("{0} extensions loaded in {1} ms.", supported, time.ElapsedMilliseconds); time.Reset(); } #endregion #region internal static bool TryLoadExtension(Type type, string extension) /// /// Loads the specified extension for the specified class. This function is intended /// for OpenGL, Wgl, Glx, OpenAL etc. /// The class to load extensions for. /// The extension to load. /// /// The Type must contain a nested class called "Delegates". /// /// The Type must also implement a static function called LoadDelegate with the /// following signature: /// static Delegate LoadDelegate(string name, Type signature) /// /// This function allocates memory. /// internal static bool TryLoadExtension(Type type, string extension) { Type extensions_class = type.GetNestedType("Delegates", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); if (extensions_class == null) { Debug.Print(type.ToString(), " does not contain extensions."); return false; } LoadDelegateFunction LoadDelegate = (LoadDelegateFunction)Delegate.CreateDelegate(typeof(LoadDelegateFunction), type.GetMethod("LoadDelegate", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)); if (LoadDelegate == null) { Debug.Print(type.ToString(), " does not contain a static LoadDelegate method."); return false; } FieldInfo f = extensions_class.GetField(extension, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); if (f == null) { Debug.Print("Extension \"", extension, "\" not found in ", type.ToString()); return false; } Delegate old = f.GetValue(null) as Delegate; Delegate @new = LoadDelegate(f.Name, f.FieldType); if ((old != null ? old.Target : null) != (@new != null ? @new.Target : null)) { f.SetValue(null, @new); FieldInfo rebuildExtensionList = type.GetField("rebuildExtensionList", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); if (rebuildExtensionList != null) rebuildExtensionList.SetValue(null, true); } return @new != null; } #endregion #region CreateGetAddress internal static GraphicsContext.GetAddressDelegate CreateGetAddress() { GraphicsContext.GetAddressDelegate loader = null; if (Configuration.RunningOnWindows) { loader = Platform.Windows.Wgl.GetProcAddress; } else if (Configuration.RunningOnX11) { loader = Platform.X11.Glx.GetProcAddress; } else if (Configuration.RunningOnMacOS) { loader = Platform.MacOS.NS.GetAddress; } else { throw new PlatformNotSupportedException(); } return loader; } #endregion #region --- Creating a Graphics Context --- /// /// Creates an IGraphicsContext instance for the specified window. /// /// The GraphicsMode for the GraphicsContext. /// An IWindowInfo instance describing the parent window for this IGraphicsContext. /// The major OpenGL version number for this IGraphicsContext. /// The minor OpenGL version number for this IGraphicsContext. /// A bitwise collection of GraphicsContextFlags with specific options for this IGraphicsContext. /// A new IGraphicsContext instance. [Obsolete("Call new OpenTK.Graphics.GraphicsContext() directly, instead.")] public static IGraphicsContext CreateGraphicsContext( GraphicsMode mode, IWindowInfo window, int major, int minor, GraphicsContextFlags flags) { GraphicsContext context = new GraphicsContext(mode, window, major, minor, flags); context.MakeCurrent(window); (context as IGraphicsContextInternal).LoadAll(); return context; } #region CreateX11WindowInfo /// /// Constructs a new IWindowInfo instance for the X11 platform. /// /// The display connection. /// The screen. /// The handle for the window. /// The root window for screen. /// A pointer to a XVisualInfo structure obtained through XGetVisualInfo. /// A new IWindowInfo instance. public static IWindowInfo CreateX11WindowInfo(IntPtr display, int screen, IntPtr windowHandle, IntPtr rootWindow, IntPtr visualInfo) { Platform.X11.X11WindowInfo window = new OpenTK.Platform.X11.X11WindowInfo(); window.Display = display; window.Screen = screen; window.Handle = windowHandle; window.RootWindow = rootWindow; window.Visual = visualInfo; return window; } #endregion #region CreateWindowsWindowInfo /// /// Creates an IWindowInfo instance for the windows platform. /// /// The handle of the window. /// A new IWindowInfo instance. public static IWindowInfo CreateWindowsWindowInfo(IntPtr windowHandle) { return new OpenTK.Platform.Windows.WinWindowInfo(windowHandle, null); } #endregion #region CreateMacOSCarbonWindowInfo /// /// Creates an IWindowInfo instance for the Mac OS X platform. /// /// The handle of the window. /// Ignored. This is reserved for future use. /// Set to true if windowHandle corresponds to a System.Windows.Forms control. /// A new IWindowInfo instance. public static IWindowInfo CreateMacOSCarbonWindowInfo(IntPtr windowHandle, bool ownHandle, bool isControl) { return CreateMacOSCarbonWindowInfo(windowHandle, ownHandle, isControl, null, null); } /// /// Creates an IWindowInfo instance for the Mac OS X platform with an X and Y offset for the GL viewport location. /// /// The handle of the window. /// Ignored. This is reserved for future use. /// Set to true if windowHandle corresponds to a System.Windows.Forms control. /// The X offset for the GL viewport /// The Y offset for the GL viewport /// A new IWindowInfo instance. public static IWindowInfo CreateMacOSCarbonWindowInfo(IntPtr windowHandle, bool ownHandle, bool isControl, OpenTK.Platform.MacOS.GetInt xOffset, OpenTK.Platform.MacOS.GetInt yOffset) { return new OpenTK.Platform.MacOS.CarbonWindowInfo(windowHandle, false, isControl, xOffset, yOffset); } #endregion #region CreateMacOSWindowInfo /// /// Creates an IWindowInfo instance for the Mac OS X platform. /// /// The handle of the NSWindow. /// Assumes that the NSWindow's contentView is the NSView we want to attach to our context. /// A new IWindowInfo instance. public static IWindowInfo CreateMacOSWindowInfo(IntPtr windowHandle) { return new OpenTK.Platform.MacOS.CocoaWindowInfo(windowHandle); } /// /// Creates an IWindowInfo instance for the Mac OS X platform. /// /// The handle of the NSWindow. /// The handle of the NSView. /// A new IWindowInfo instance. public static IWindowInfo CreateMacOSWindowInfo(IntPtr windowHandle, IntPtr viewHandle) { return new OpenTK.Platform.MacOS.CocoaWindowInfo(windowHandle, viewHandle); } #endregion #region CreateDummyWindowInfo /// /// Creates an IWindowInfo instance for the dummy platform. /// /// A new IWindowInfo instance. public static IWindowInfo CreateDummyWindowInfo() { return new Dummy.DummyWindowInfo(); } #endregion #region CreateSdl2WindowInfo /// /// Creates an IWindowInfo instance for the windows platform. /// /// The handle of the window. /// A new IWindowInfo instance. public static IWindowInfo CreateSdl2WindowInfo(IntPtr windowHandle) { return new OpenTK.Platform.SDL2.Sdl2WindowInfo( windowHandle, null); } #endregion #endregion #region RelaxGraphicsMode internal static bool RelaxGraphicsMode(ref GraphicsMode mode) { ColorFormat color = mode.ColorFormat; int depth = mode.Depth; int stencil = mode.Stencil; int samples = mode.Samples; ColorFormat accum = mode.AccumulatorFormat; int buffers = mode.Buffers; bool stereo = mode.Stereo; bool success = RelaxGraphicsMode( ref color, ref depth, ref stencil, ref samples, ref accum, ref buffers, ref stereo); mode = new GraphicsMode( color, depth, stencil, samples, accum, buffers, stereo); return success; } /// \internal /// /// Relaxes graphics mode parameters. Use this function to increase compatibility /// on systems that do not directly support a requested GraphicsMode. For example: /// - user requested stereoscopic rendering, but GPU does not support stereo /// - user requseted 16x antialiasing, but GPU only supports 4x /// /// true, if a graphics mode parameter was relaxed, false otherwise. /// Color bits. /// Depth bits. /// Stencil bits. /// Number of antialiasing samples. /// Accumulator buffer bits. /// Number of rendering buffers (1 for single buffering, 2+ for double buffering, 0 for don't care). /// Stereoscopic rendering enabled/disabled. internal static bool RelaxGraphicsMode(ref ColorFormat color, ref int depth, ref int stencil, ref int samples, ref ColorFormat accum, ref int buffers, ref bool stereo) { // Parameters are relaxed in order of importance. // - Accumulator buffers are way outdated as a concept, // so they go first. // - Triple+ buffering is generally not supported by the // core WGL/GLX/AGL/CGL/EGL specs, so we clamp // to double-buffering as a second step. (If this doesn't help // we will also fall back to undefined single/double buffering // as a last resort). // - AA samples are an easy way to increase compatibility // so they go next. // - Stereoscopic is only supported on very few GPUs // (Quadro/FirePro series) so it goes next. // - The rest of the parameters then follow. if (accum != 0) { accum = 0; return true; } if (buffers > 2) { buffers = 2; return true; } if (samples > 0) { samples = Math.Max(samples - 1, 0); return true; } if (stereo) { stereo = false; return true; } if (stencil != 0) { stencil = 0; return true; } if (depth != 0) { depth = 0; return true; } if (color != 24) { color = 24; return true; } if (buffers != 0) { buffers = 0; return true; } // no parameters left to relax, fail return false; } #endregion } }