#region License // // The Open Toolkit Library License // // Copyright (c) 2006 - 2009 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 using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Reflection; namespace OpenTK { /// Provides information about the underlying OS and runtime. public static class Configuration { static bool runningOnWindows, runningOnUnix, runningOnX11, runningOnMacOS, runningOnLinux, runningOnMono; volatile static bool initialized; readonly static object InitLock = new object(); #region Constructors // Detects the underlying OS and runtime. static Configuration() { Toolkit.Init(); } #endregion #region Public Methods #region public static bool RunningOnWindows /// Gets a System.Boolean indicating whether OpenTK is running on a Windows platform. public static bool RunningOnWindows { get { return runningOnWindows; } } #endregion #region public static bool RunningOnX11 /// Gets a System.Boolean indicating whether OpenTK is running on an X11 platform. public static bool RunningOnX11 { get { return runningOnX11; } } /// /// Gets a indicating whether OpenTK is running on a Unix platform. /// public static bool RunningOnUnix { get { return runningOnUnix; } } #endregion #region public static bool RunningOnLinux /// Gets a System.Boolean indicating whether OpenTK is running on an X11 platform. public static bool RunningOnLinux { get { return runningOnLinux; } } #endregion #region public static bool RunningOnMacOS /// Gets a System.Boolean indicating whether OpenTK is running on a MacOS platform. public static bool RunningOnMacOS { get { return runningOnMacOS; } } #endregion #region public static bool RunningOnMono /// /// Gets a System.Boolean indicating whether OpenTK is running on the Mono runtime. /// public static bool RunningOnMono { get { return runningOnMono; } } #endregion #region --- Private Methods --- #region private static string DetectUnixKernel() [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] struct utsname { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string sysname; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string nodename; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string release; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string version; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string machine; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)] public string extraJustInCase; } /// /// Detects the unix kernel by p/invoking uname (libc). /// /// private static string DetectUnixKernel() { Debug.Print("Size: {0}", Marshal.SizeOf(typeof(utsname)).ToString()); Debug.Flush(); utsname uts = new utsname(); uname(out uts); Debug.WriteLine("System:"); Debug.Indent(); Debug.WriteLine(uts.sysname); Debug.WriteLine(uts.nodename); Debug.WriteLine(uts.release); Debug.WriteLine(uts.version); Debug.WriteLine(uts.machine); Debug.Unindent(); return uts.sysname.ToString(); } [DllImport("libc")] private static extern void uname(out utsname uname_struct); #endregion #endregion #endregion #region Internal Methods internal static void Init() { lock (InitLock) { if (!initialized) { initialized = true; if (System.Environment.OSVersion.Platform == PlatformID.Win32NT || System.Environment.OSVersion.Platform == PlatformID.Win32S || System.Environment.OSVersion.Platform == PlatformID.Win32Windows || System.Environment.OSVersion.Platform == PlatformID.WinCE) runningOnWindows = true; // Write config file before using any p/invokes, otherwise Mono won't pick it up. if (!RunningOnWindows) { WriteConfigFile(); } else if (System.Environment.OSVersion.Platform == PlatformID.Unix || System.Environment.OSVersion.Platform == (PlatformID)4) { // Distinguish between Linux, Mac OS X and other Unix operating systems. string kernel_name = DetectUnixKernel(); switch (kernel_name) { case null: case "": throw new PlatformNotSupportedException( "Unknown platform. Please file a bug report at http://www.opentk.com/node/add/project-issue/opentk"); case "Linux": runningOnLinux = runningOnUnix = true; break; case "Darwin": runningOnMacOS = runningOnUnix = true; break; default: runningOnUnix = true; break; } } else throw new PlatformNotSupportedException("Unknown platform. Please report this error at http://www.opentk.com."); // Detect whether X is present. // Hack: it seems that this check will cause X to initialize itself on Mac OS X Leopard and newer. // We don't want that (we'll be using the native interfaces anyway), so we'll avoid this check // when we detect Mac OS X. if (!RunningOnMacOS) { try { runningOnX11 = OpenTK.Platform.X11.API.DefaultDisplay != IntPtr.Zero; } catch { } } // Detect the Mono runtime (code taken from http://mono.wikia.com/wiki/Detecting_if_program_is_running_in_Mono). Type t = Type.GetType("Mono.Runtime"); if (t != null) runningOnMono = true; Debug.Print("Detected configuration: {0} / {1}", RunningOnWindows ? "Windows" : RunningOnLinux ? "Linux" : RunningOnMacOS ? "MacOS" : runningOnUnix ? "Unix" : RunningOnX11 ? "X11" : "Unknown Platform", RunningOnMono ? "Mono" : ".Net"); } } } #endregion #region Private Methods // Creates path on disk if it doesn't already exist static void CreatePath(string path) { if (!Directory.Exists(path)) Directory.CreateDirectory(path); } // Write OpenTK.dll.config to disk when running on non-Windows platforms. // Mono will automatically load the config from ~/.mono/assemblies/OpenTK, // which is great when the user forgets to copy the file on his own static void WriteConfigFile() { Debug.Write("Writing config file "); string name = null; string monopath = null; string asmpath = null; string outpath = null; string file_old = null; string file = null; try { Assembly asm = Assembly.GetAssembly(typeof(Configuration)); name = asm.GetName().Name; // In case someone embeds OpenTK into another dll. monopath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.Personal), ".mono"); asmpath = Path.Combine(monopath, "assemblies"); outpath = Path.Combine(asmpath, name); file_old = Path.ChangeExtension(name, "dll.config"); // For mono version prior to 1.9 file = Path.ChangeExtension(name, "config"); // For mono 1.9 and higher Debug.Print("to {0}/{1}.", outpath, file); CreatePath(monopath); CreatePath(asmpath); CreatePath(outpath); Debug.Print("Loading embedded config."); // Note: the resource name and namespace are hardcoded. // This will fail if someone recompiles OpenTK with a different namespace // (not *our* problem, though). Stream str = asm.GetManifestResourceStream("OpenTK.OpenTK.dll.config"); if (str != null) { using (str) { byte[] buffer = new byte[str.Length]; str.Read(buffer, 0, buffer.Length); string config = System.Text.UnicodeEncoding.Default.GetString(buffer); System.IO.File.WriteAllText(Path.Combine(outpath, file), config); System.IO.File.WriteAllText(Path.Combine(outpath, file_old), config); } Debug.Print("Success!"); } else { Debug.Print("[Warning] Failed, embedded config not found."); } } catch (Exception e) { Debug.Print("[Warning] Failed to write {0} to \"{1}\". Error: {2}", file, outpath, e); } } #endregion } }