#region --- License --- /* Copyright (c) 2007 Stefanos Apostolopoulos * See license.txt for license info */ #endregion using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.Diagnostics; using OpenTK.OpenGL; namespace OpenTK.Platform.X11 { /// /// Provides methods to create and control an opengl context on the X11 platform. /// This class supports OpenTK, and is not intended for use by OpenTK programs. /// internal sealed class X11GLContext : IGLContext, IGLContextInternal, IGLContextCreationHack { IntPtr context; DisplayMode mode; WindowInfo windowInfo = new WindowInfo(); IntPtr visual; bool vsync_supported; int vsync_interval; bool disposed; #region --- Constructors --- /// /// Constructs a new X11GLContext object. /// public X11GLContext() { } #endregion #region --- IGLContextCreationHack Members --- #region bool IGLContextCreationHack.SelectDisplayMode(DisplayMode mode, IWindowInfo info) /// /// HACK! This function will be removed in 0.3.15 /// Checks if the specified OpenTK.Platform.DisplayMode is available, and selects it if it is. /// /// The OpenTK.Platform.DisplayMode to select. /// The OpenTK.Platform.IWindowInfo that describes the display to use. Note: a window handle is not necessary for this function! /// True if the DisplayMode is available, false otherwise. bool IGLContextCreationHack.SelectDisplayMode(DisplayMode mode, IWindowInfo info) { List visualAttributes = new List(); // TODO: Improve modesetting code. if (mode == null || mode.Color.BitsPerPixel == 0) { // Define the bare essentials - needed for compatibility with Mono's System.Windows.Forms Debug.Print("Preparing visual for System.Windows.Forms (compatibility mode)"); visualAttributes.Add((int)Glx.Enums.GLXAttribute.RGBA); /*visualAttributes.Add((int)Glx.Enums.GLXAttribute.RED_SIZE); visualAttributes.Add((int)1); visualAttributes.Add((int)Glx.Enums.GLXAttribute.GREEN_SIZE); visualAttributes.Add((int)1); visualAttributes.Add((int)Glx.Enums.GLXAttribute.BLUE_SIZE); visualAttributes.Add((int)1);*/ visualAttributes.Add((int)Glx.Enums.GLXAttribute.DEPTH_SIZE); visualAttributes.Add((int)1); visualAttributes.Add((int)Glx.Enums.GLXAttribute.DOUBLEBUFFER); visualAttributes.Add((int)0); } else { Debug.Print("Preparing visual for DisplayMode: {0}", mode.ToString()); visualAttributes.Add((int)Glx.Enums.GLXAttribute.RGBA); visualAttributes.Add((int)Glx.Enums.GLXAttribute.RED_SIZE); visualAttributes.Add((int)mode.Color.Red); visualAttributes.Add((int)Glx.Enums.GLXAttribute.GREEN_SIZE); visualAttributes.Add((int)mode.Color.Green); visualAttributes.Add((int)Glx.Enums.GLXAttribute.BLUE_SIZE); visualAttributes.Add((int)mode.Color.Blue); visualAttributes.Add((int)Glx.Enums.GLXAttribute.ALPHA_SIZE); visualAttributes.Add((int)mode.Color.Alpha); visualAttributes.Add((int)Glx.Enums.GLXAttribute.DEPTH_SIZE); visualAttributes.Add((int)mode.Depth); visualAttributes.Add((int)Glx.Enums.GLXAttribute.DOUBLEBUFFER); visualAttributes.Add((int)0); } windowInfo.CopyInfoFrom(info); visual = Glx.ChooseVisual(windowInfo.Display, windowInfo.Screen, visualAttributes.ToArray()); if (visual == IntPtr.Zero) return false; else { windowInfo.VisualInfo = (XVisualInfo)Marshal.PtrToStructure(visual, typeof(XVisualInfo)); Debug.Print("Chose visual {0}", windowInfo.VisualInfo.ToString()); } return true; } #endregion void IGLContextCreationHack.SetWindowHandle(IntPtr handle) { this.windowInfo.Handle = handle; } #endregion #region --- IGLContext Members --- #region public DisplayMode Mode public DisplayMode Mode { get { return mode; } private set { if (context == IntPtr.Zero) mode = value; else Debug.Print("Cannot change DisplayMode of an existing context."); } } #endregion public void CreateContext() { this.CreateContext(true, null); } public void CreateContext(bool direct) { this.CreateContext(direct, null); } #region public void CreateContext(bool direct, IGLContext shareContext) public void CreateContext(bool direct, IGLContext shareContext) { try { Debug.WriteLine("Creating opengl context."); Debug.Indent(); ContextHandle shareHandle = shareContext != null ? (shareContext as IGLContextInternal).Context : (ContextHandle)IntPtr.Zero; Debug.Write(direct ? "Context is direct, " : "Context is indirect, "); Debug.WriteLine(shareHandle.Handle == IntPtr.Zero ? "not shared." : String.Format("shared with ({0}).", shareHandle)); // Try to call Glx.CreateContext with the specified parameters. context = Glx.CreateContext(windowInfo.Display, visual, shareHandle.Handle, direct); // Context creation succeeded, return. if (context != IntPtr.Zero) { Debug.Print("New opengl context created. (id: {0})", context); this.MakeCurrent(); GL.LoadAll(); Glu.LoadAll(); Glx.LoadAll(); return; } // Context creation failed. Retry with a non-shared context with the // direct/indirect rendering mode flipped. Debug.Print("Cotnext creation failed, retrying with a non-shared, {0} context.", !direct ? "direct" : "indirect"); context = Glx.CreateContext(windowInfo.Display, visual, IntPtr.Zero, !direct); if (context != IntPtr.Zero) { Debug.Print("New opengl context created. (id: {0})", context); this.MakeCurrent(); GL.LoadAll(); Glu.LoadAll(); Glx.LoadAll(); return; } vsync_supported = Glx.SupportsExtension("glxSwapControlSGI"); throw new ApplicationException("Glx.CreateContext call failed (returned 0)."); } finally { Debug.Unindent(); } } #endregion #region public void SwapBuffers() public void SwapBuffers() { Glx.SwapBuffers(windowInfo.Display, windowInfo.Handle); } #endregion #region public void MakeCurrent() bool result; public void MakeCurrent() { Debug.Write(String.Format("Making context {0} current on thread {1} (Display: {2}, Screen: {3}, Window: {4})... ", context, System.Threading.Thread.CurrentThread.ManagedThreadId, windowInfo.Display, windowInfo.Screen, windowInfo.Handle)); if (windowInfo.Display != IntPtr.Zero && windowInfo.Handle != IntPtr.Zero && context != IntPtr.Zero) { result = Glx.MakeCurrent(windowInfo.Display, windowInfo.Handle, context); if (!result) { Debug.WriteLine("failed."); // probably need to recreate context here. //throw new ApplicationException(String.Format("Failed to make context {0} current on thread {1}.", // context, System.Threading.Thread.CurrentThread.ManagedThreadId)); } else { Debug.WriteLine("done!"); } } } #endregion #region public bool IsCurrent public bool IsCurrent { get { return Glx.GetCurrentContext() == this.context; } } #endregion #region public bool VSync public bool VSync { get { return vsync_supported && vsync_interval > 0; } set { if (vsync_supported) { int error_code = Glx.Sgi.SwapInterval(value ? 1 : 0); if (error_code != 0) throw new GraphicsException(String.Format("Could not set vsync, error code: {0}", error_code)); vsync_interval = value ? 1 : 0; } } } #endregion public event DestroyEvent Destroy; #region public IntPtr GetAddress(string function) public IntPtr GetAddress(string function) { return Glx.GetProcAddress(function); } #endregion public void RegisterForDisposal(IDisposable resource) { throw new NotSupportedException("Use OpenTK.GLContext instead."); } public void DisposeResources() { throw new NotSupportedException("Use OpenTK.GLContext instead."); } public IEnumerable GetDisplayModes() { throw new Exception("The method or operation is not implemented."); } #endregion #region --- IGLContextInternal Members --- #region IntPtr IGLContextInternal.Context ContextHandle IGLContextInternal.Context { get { return context; } /*private set { context = value; }*/ } #endregion #region IWindowInfo IGLContextInternal.Info IWindowInfo IGLContextInternal.Info { get { return windowInfo; } } #endregion #region ContextHandle IGLContextInternal.GetCurrentContext() ContextHandle IGLContextInternal.GetCurrentContext() { return (ContextHandle)Glx.GetCurrentContext(); } #endregion #endregion #region --- Public Methods --- void OnDestroy() { if (Destroy != null) Destroy(this, EventArgs.Empty); } #endregion #region --- IDisposable Members --- public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool manuallyCalled) { if (!disposed) { // Clean unmanaged resources: Glx.MakeCurrent(windowInfo.Display, IntPtr.Zero, IntPtr.Zero); Glx.DestroyContext(windowInfo.Display, context); API.Free(visual); if (manuallyCalled) { // Safe to clean managed resources, too } } disposed = true; } ~X11GLContext() { this.Dispose(false); } #endregion } }