diff --git a/Source/Examples/OpenTK.Examples.csproj b/Source/Examples/OpenTK.Examples.csproj
index 4871f240..c78290e6 100644
--- a/Source/Examples/OpenTK.Examples.csproj
+++ b/Source/Examples/OpenTK.Examples.csproj
@@ -565,6 +565,7 @@
Dependencies\x64\libSDL2.dylib
+
diff --git a/Source/Examples/OpenTK/Test/ExternalContext.cs b/Source/Examples/OpenTK/Test/ExternalContext.cs
new file mode 100644
index 00000000..cdb6d156
--- /dev/null
+++ b/Source/Examples/OpenTK/Test/ExternalContext.cs
@@ -0,0 +1,81 @@
+// This code was written for the OpenTK library and has been released
+// to the Public Domain.
+// It is provided "as is" without express or implied warranty of any kind.
+
+using System;
+using System.Runtime.InteropServices;
+using OpenTK;
+using OpenTK.Graphics;
+using OpenTK.Graphics.OpenGL;
+
+namespace Examples.Tests
+{
+ [Example("External Context Test", ExampleCategory.OpenTK, "OpenGL")]
+ class ExternalContext
+ {
+ public static void Main()
+ {
+ using (Toolkit.Init(new ToolkitOptions { Backend = PlatformBackend.PreferNative }))
+ {
+ var window = Sdl2.CreateWindow("Test", 0, 0, 640, 480, WindowFlags.AllowHighDpi | WindowFlags.OpenGL);
+ var context = Sdl2.CreateContext(window);
+ Sdl2.MakeCurrent(window, context);
+
+ using (var dummy = new GraphicsContext(new ContextHandle(context), OpenTK.Platform.Utilities.CreateDummyWindowInfo()))
+ {
+ for (int i = 0; i < 100; i++)
+ {
+ Sdl2.PumpEvents();
+ GL.ClearColor(i / 100.0f, i / 100.0f, i / 100.0f, i / 100.0f);
+ GL.Clear(ClearBufferMask.ColorBufferBit);
+
+ Sdl2.SwapWindow(window);
+ }
+
+ Sdl2.DestroyWindow(window);
+ }
+ }
+ }
+ }
+
+ #region SDL2 bindings
+
+ public enum WindowFlags
+ {
+ Default = 0,
+ OpenGL = 0x00000002,
+ AllowHighDpi = 0x00002000,
+ }
+
+ static class Sdl2
+ {
+ const string lib = "SDL2.dll";
+
+ [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_CreateWindow", ExactSpelling = true)]
+ public static extern IntPtr CreateWindow(string title, int x, int y, int w, int h, WindowFlags flags);
+
+ [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_CreateContext", ExactSpelling = true)]
+ public static extern IntPtr CreateContext(IntPtr window);
+
+ [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_DestroyWindow", ExactSpelling = true)]
+ public static extern void DestroyWindow(IntPtr window);
+
+ [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetCurrentContext", ExactSpelling = true)]
+ public static extern IntPtr GetCurrentContext();
+
+ [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetProcAddress", ExactSpelling = true)]
+ public static extern IntPtr GetAddress(string name);
+
+ [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_MakeCurrent", ExactSpelling = true)]
+ public static extern int MakeCurrent(IntPtr window, IntPtr context);
+
+ [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_PumpEvents", ExactSpelling = true)]
+ public static extern void PumpEvents();
+
+ [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_SwapWindow", ExactSpelling = true)]
+ public static extern void SwapWindow(IntPtr window);
+ }
+
+ #endregion
+
+}
diff --git a/Source/OpenTK/Graphics/GraphicsContext.cs b/Source/OpenTK/Graphics/GraphicsContext.cs
index 6c0b93f6..1fd39b2d 100644
--- a/Source/OpenTK/Graphics/GraphicsContext.cs
+++ b/Source/OpenTK/Graphics/GraphicsContext.cs
@@ -39,14 +39,13 @@ namespace OpenTK.Graphics
///
public sealed class GraphicsContext : IGraphicsContext, IGraphicsContextInternal
{
+ public delegate IntPtr GetAddressDelegate(string function);
+ public delegate ContextHandle GetCurrentContextDelegate();
+
#region --- Fields ---
IGraphicsContext implementation; // The actual render context implementation for the underlying platform.
bool disposed;
- // Indicates that this context was created through external means, e.g. Tao.Sdl or GLWidget#.
- // In this case, We'll assume that the external program will manage the lifetime of this
- // context - we'll not destroy it manually.
- readonly bool IsExternal;
bool check_errors = true;
// Cache for the context handle. We need this for RemoveContext()
// in case the user does not call Dispose(). When this happens,
@@ -67,17 +66,6 @@ namespace OpenTK.Graphics
#region --- Constructors ---
- // Necessary to allow creation of dummy GraphicsContexts (see CreateDummyContext static method).
- GraphicsContext(ContextHandle handle)
- {
- implementation = new OpenTK.Platform.Dummy.DummyGLContext(handle);
-
- lock (SyncRoot)
- {
- AddContext(this);
- }
- }
-
///
/// Constructs a new GraphicsContext with the specified GraphicsMode and attaches it to the specified window.
///
@@ -163,12 +151,65 @@ namespace OpenTK.Graphics
}
}
+ ///
+ /// Initializes a new instance of the class using
+ /// an external context handle that was created by a third-party library.
+ ///
+ ///
+ /// A valid, unique handle for an external OpenGL context, or ContextHandle.Zero to use the current context.
+ /// It is an error to specify a handle that has been created through OpenTK or that has been passed to OpenTK before.
+ ///
+ ///
+ /// A GetAddressDelegate instance that accepts the name of an OpenGL function and returns
+ /// a valid function pointer, or IntPtr.Zero if that function is not supported. This delegate should be
+ /// implemented using the same toolkit that created the OpenGL context (i.e. if the context was created with
+ /// SDL_GL_CreateContext(), then this delegate should use SDL_GL_GetProcAddress() to retrieve function
+ /// pointers.)
+ ///
+ ///
+ /// A GetCurrentContextDelegate instance that returns the handle of the current OpenGL context,
+ /// or IntPtr.Zero if no context is current on the calling thread. This delegate should be implemented
+ /// using the same toolkit that created the OpenGL context (i.e. if the context was created with
+ /// SDL_GL_CreateContext(), then this delegate should use SDL_GL_GetCurrentContext() to retrieve
+ /// the current context.)
+ ///
+ public GraphicsContext(ContextHandle handle, GetAddressDelegate getAddress, GetCurrentContextDelegate getCurrent)
+ {
+ if (getAddress == null || getCurrent == null)
+ throw new ArgumentNullException();
+
+ lock (SyncRoot)
+ {
+ // Replace a zero-handle by the current context, if any
+ if (handle == ContextHandle.Zero)
+ {
+ handle = getCurrent();
+ }
+
+ // Make sure this handle corresponds to a valid, unique OpenGL context
+ if (handle == ContextHandle.Zero)
+ {
+ throw new GraphicsContextMissingException();
+ }
+ else if (available_contexts.ContainsKey(handle))
+ {
+ throw new InvalidOperationException("Context handle has already been added");
+ }
+
+ // We have a valid handle for an external OpenGL context, wrap it into a
+ // DummyGLContext instance.
+ implementation = new Platform.Dummy.DummyGLContext(handle, getAddress);
+ GetCurrentContext = getCurrent ?? GetCurrentContext;
+ AddContext(this);
+ }
+ implementation.LoadAll();
+ }
+
///
/// Constructs a new GraphicsContext from a pre-existing context created outside of OpenTK.
///
/// The handle of the existing context. This must be a valid, unique handle that is not known to OpenTK.
- /// The window this context is bound to. This must be a valid window obtained through Utilities.CreateWindowInfo.
- /// Occurs if handle is identical to a context already registered with OpenTK.
+ /// This parameter is reserved.
public GraphicsContext(ContextHandle handle, IWindowInfo window)
: this(handle, window, null, 1, 0, GraphicsContextFlags.Default)
{ }
@@ -177,40 +218,14 @@ namespace OpenTK.Graphics
/// Constructs a new GraphicsContext from a pre-existing context created outside of OpenTK.
///
/// The handle of the existing context. This must be a valid, unique handle that is not known to OpenTK.
- /// The window this context is bound to. This must be a valid window obtained through Utilities.CreateWindowInfo.
- /// A different context that shares resources with this instance, if any.
- /// Pass null if the context is not shared or if this is the first GraphicsContext instruct you construct.
- /// The major version of the context (e.g. "2" for "2.1").
- /// The minor version of the context (e.g. "1" for "2.1").
- /// A bitwise combination of that describe this context.
- /// Occurs if handle is identical to a context already registered with OpenTK.
+ /// This parameter is reserved.
+ /// This parameter is reserved.
+ /// This parameter is reserved.
+ /// This parameter is reserved.
+ /// This parameter is reserved..
public GraphicsContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, int major, int minor, GraphicsContextFlags flags)
- : this(handle)
- {
- lock (SyncRoot)
- {
- IsExternal = true;
-
- if (handle == ContextHandle.Zero)
- {
- implementation = new OpenTK.Platform.Dummy.DummyGLContext(handle);
- }
- else if (available_contexts.ContainsKey(handle))
- {
- throw new GraphicsContextException("Context already exists.");
- }
- else
- {
- switch ((flags & GraphicsContextFlags.Embedded) == GraphicsContextFlags.Embedded)
- {
- case false: implementation = Factory.Default.CreateGLContext(handle, window, shareContext, direct_rendering, major, minor, flags); break;
- case true: implementation = Factory.Embedded.CreateGLContext(handle, window, shareContext, direct_rendering, major, minor, flags); break;
- }
- }
-
- (this as IGraphicsContextInternal).LoadAll();
- }
- }
+ : this(handle, Platform.Utilities.CreateGetAddress(), Factory.Default.CreateGetCurrentGraphicsContext())
+ { }
#endregion
@@ -309,6 +324,7 @@ namespace OpenTK.Graphics
/// Instances created by this method will not be functional. Instance methods will have no effect.
/// This method requires that a context is current on the calling thread.
///
+ [Obsolete("Use GraphicsContext(ContextHandle, IWindowInfo) constructor instead")]
public static GraphicsContext CreateDummyContext()
{
ContextHandle handle = GetCurrentContext();
@@ -326,12 +342,13 @@ namespace OpenTK.Graphics
///
/// Instances created by this method will not be functional. Instance methods will have no effect.
///
+ [Obsolete("Use GraphicsContext(ContextHandle, IWindowInfo) constructor instead")]
public static GraphicsContext CreateDummyContext(ContextHandle handle)
{
if (handle == ContextHandle.Zero)
throw new ArgumentOutOfRangeException("handle");
- return new GraphicsContext(handle);
+ return new GraphicsContext(handle, (IWindowInfo)null);
}
#endregion
@@ -352,7 +369,6 @@ namespace OpenTK.Graphics
#region public static IGraphicsContext CurrentContext
- internal delegate ContextHandle GetCurrentContextDelegate();
internal static GetCurrentContextDelegate GetCurrentContext;
///
diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj
index 7bddfcb0..92404b7f 100644
--- a/Source/OpenTK/OpenTK.csproj
+++ b/Source/OpenTK/OpenTK.csproj
@@ -777,6 +777,7 @@
+
diff --git a/Source/OpenTK/Platform/Dummy/DummyGLContext.cs b/Source/OpenTK/Platform/Dummy/DummyGLContext.cs
index 05853f79..bb6f1a2a 100644
--- a/Source/OpenTK/Platform/Dummy/DummyGLContext.cs
+++ b/Source/OpenTK/Platform/Dummy/DummyGLContext.cs
@@ -22,7 +22,8 @@ namespace OpenTK.Platform.Dummy
///
internal sealed class DummyGLContext : DesktopGraphicsContext
{
- // This mode is not real. To receive a real mode we'd have to create a temporary context, which is not desirable!
+ readonly GraphicsContext.GetAddressDelegate Loader;
+
bool vsync;
int swap_interval;
static int handle_count;
@@ -31,13 +32,20 @@ namespace OpenTK.Platform.Dummy
#region --- Constructors ---
public DummyGLContext()
- : this(new ContextHandle(new IntPtr(++handle_count)))
{
+ Handle = new ContextHandle(
+ new IntPtr(Interlocked.Increment(
+ ref handle_count)));
}
-
- public DummyGLContext(ContextHandle handle)
+
+ public DummyGLContext(ContextHandle handle, GraphicsContext.GetAddressDelegate loader)
+ : this()
{
- Handle = handle;
+ if (handle != ContextHandle.Zero)
+ {
+ Handle = handle;
+ }
+ Loader = loader;
Mode = new GraphicsMode(new IntPtr(2), 32, 16, 0, 0, 0, 2, false);
}
@@ -45,15 +53,6 @@ namespace OpenTK.Platform.Dummy
#region --- IGraphicsContext Members ---
- public void CreateContext(bool direct, IGraphicsContext source)
- {
- if (Handle == ContextHandle.Zero)
- {
- ++handle_count;
- Handle = new ContextHandle((IntPtr)handle_count);
- }
- }
-
public override void SwapBuffers() { }
public override void MakeCurrent(IWindowInfo info)
@@ -81,9 +80,15 @@ namespace OpenTK.Platform.Dummy
get { return current_thread != null && current_thread == Thread.CurrentThread; }
}
- public override IntPtr GetAddress(string function) { return IntPtr.Zero; }
+ public override IntPtr GetAddress(string function)
+ {
+ return Loader(function);
+ }
- public override IntPtr GetAddress(IntPtr function) { return IntPtr.Zero; }
+ public override IntPtr GetAddress(IntPtr function)
+ {
+ return IntPtr.Zero;
+ }
public override int SwapInterval
{
@@ -101,7 +106,14 @@ namespace OpenTK.Platform.Dummy
{ }
public override void LoadAll()
- { }
+ {
+ new OpenTK.Graphics.OpenGL.GL().LoadEntryPoints();
+ new OpenTK.Graphics.OpenGL4.GL().LoadEntryPoints();
+ new OpenTK.Graphics.ES10.GL().LoadEntryPoints();
+ new OpenTK.Graphics.ES11.GL().LoadEntryPoints();
+ new OpenTK.Graphics.ES20.GL().LoadEntryPoints();
+ new OpenTK.Graphics.ES30.GL().LoadEntryPoints();
+ }
#endregion
diff --git a/Source/OpenTK/Platform/MacOS/AglContext.cs b/Source/OpenTK/Platform/MacOS/AglContext.cs
index 65bdd0af..d1de6664 100644
--- a/Source/OpenTK/Platform/MacOS/AglContext.cs
+++ b/Source/OpenTK/Platform/MacOS/AglContext.cs
@@ -464,64 +464,16 @@ namespace OpenTK.Platform.MacOS
#region IGraphicsContextInternal Members
- private const string Library = "libdl.dylib";
-
- [DllImport(Library, EntryPoint = "NSIsSymbolNameDefined")]
- private static extern bool NSIsSymbolNameDefined(string s);
- [DllImport(Library, EntryPoint = "NSIsSymbolNameDefined")]
- private static extern bool NSIsSymbolNameDefined(IntPtr s);
- [DllImport(Library, EntryPoint = "NSLookupAndBindSymbol")]
- private static extern IntPtr NSLookupAndBindSymbol(string s);
- [DllImport(Library, EntryPoint = "NSLookupAndBindSymbol")]
- private static extern IntPtr NSLookupAndBindSymbol(IntPtr s);
- [DllImport(Library, EntryPoint = "NSAddressOfSymbol")]
- private static extern IntPtr NSAddressOfSymbol(IntPtr symbol);
-
public override IntPtr GetAddress(string function)
{
- // Instead of allocating and combining strings in managed memory
- // we do that directly in unmanaged memory. This way, we avoid
- // 2 string allocations every time this function is called.
-
- // must add a '_' prefix and null-terminate the function name,
- // hence we allocate +2 bytes
- IntPtr ptr = Marshal.AllocHGlobal(function.Length + 2);
- try
- {
- Marshal.WriteByte(ptr, (byte)'_');
- for (int i = 0; i < function.Length; i++)
- {
- Marshal.WriteByte(ptr, i + 1, (byte)function[i]);
- }
- Marshal.WriteByte(ptr, function.Length + 1, 0); // null-terminate
-
- IntPtr symbol = IntPtr.Zero;
- if (NSIsSymbolNameDefined(ptr))
- {
- symbol = NSLookupAndBindSymbol(ptr);
- if (symbol != IntPtr.Zero)
- symbol = NSAddressOfSymbol(symbol);
- }
- return symbol;
- }
- finally
- {
- Marshal.FreeHGlobal(ptr);
- }
+ return NS.GetAddress(function);
}
public override IntPtr GetAddress(IntPtr function)
{
- if (!NSIsSymbolNameDefined(function))
- return IntPtr.Zero;
-
- IntPtr symbol = NSLookupAndBindSymbol(function);
- if (symbol != IntPtr.Zero)
- symbol = NSAddressOfSymbol(symbol);
-
- return symbol;
+ return NS.GetAddress(function);
}
-
+
#endregion
}
}
diff --git a/Source/OpenTK/Platform/MacOS/MacOSFactory.cs b/Source/OpenTK/Platform/MacOS/MacOSFactory.cs
index ad540311..97473c23 100644
--- a/Source/OpenTK/Platform/MacOS/MacOSFactory.cs
+++ b/Source/OpenTK/Platform/MacOS/MacOSFactory.cs
@@ -70,7 +70,7 @@ namespace OpenTK.Platform.MacOS
{
return (GraphicsContext.GetCurrentContextDelegate)delegate
{
- return new ContextHandle(Agl.aglGetCurrentContext());
+ return new ContextHandle(Cgl.GetCurrentContext());
};
}
diff --git a/Source/OpenTK/Platform/MacOS/NS.cs b/Source/OpenTK/Platform/MacOS/NS.cs
new file mode 100644
index 00000000..945a7e25
--- /dev/null
+++ b/Source/OpenTK/Platform/MacOS/NS.cs
@@ -0,0 +1,92 @@
+#region License
+//
+// NS.cs
+//
+// Author:
+// Stefanos A.
+//
+// Copyright (c) 2006-2013 Stefanos Apostolopoulos
+//
+// 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.Runtime.InteropServices;
+
+namespace OpenTK.Platform.MacOS
+{
+
+ internal class NS
+ {
+ const string Library = "libdl.dylib";
+
+ [DllImport(Library, EntryPoint = "NSIsSymbolNameDefined")]
+ static extern bool NSIsSymbolNameDefined(string s);
+ [DllImport(Library, EntryPoint = "NSIsSymbolNameDefined")]
+ static extern bool NSIsSymbolNameDefined(IntPtr s);
+ [DllImport(Library, EntryPoint = "NSLookupAndBindSymbol")]
+ static extern IntPtr NSLookupAndBindSymbol(string s);
+ [DllImport(Library, EntryPoint = "NSLookupAndBindSymbol")]
+ static extern IntPtr NSLookupAndBindSymbol(IntPtr s);
+ [DllImport(Library, EntryPoint = "NSAddressOfSymbol")]
+ static extern IntPtr NSAddressOfSymbol(IntPtr symbol);
+
+ public static IntPtr GetAddress(string function)
+ {
+ // Instead of allocating and combining strings in managed memory
+ // we do that directly in unmanaged memory. This way, we avoid
+ // 2 string allocations every time this function is called.
+
+ // must add a '_' prefix and null-terminate the function name,
+ // hence we allocate +2 bytes
+ IntPtr ptr = Marshal.AllocHGlobal(function.Length + 2);
+ try
+ {
+ Marshal.WriteByte(ptr, (byte)'_');
+ for (int i = 0; i < function.Length; i++)
+ {
+ Marshal.WriteByte(ptr, i + 1, (byte)function[i]);
+ }
+ Marshal.WriteByte(ptr, function.Length + 1, 0); // null-terminate
+
+ IntPtr symbol = GetAddress(ptr);
+ return symbol;
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(ptr);
+ }
+ }
+
+ public static IntPtr GetAddress(IntPtr function)
+ {
+ IntPtr symbol = IntPtr.Zero;
+ if (NSIsSymbolNameDefined(function))
+ {
+ symbol = NSLookupAndBindSymbol(function);
+ if (symbol != IntPtr.Zero)
+ symbol = NSAddressOfSymbol(symbol);
+ }
+ return symbol;
+ }
+ }
+}
+
diff --git a/Source/OpenTK/Platform/SDL2/Sdl2.cs b/Source/OpenTK/Platform/SDL2/Sdl2.cs
index 9bf07558..40ec3700 100644
--- a/Source/OpenTK/Platform/SDL2/Sdl2.cs
+++ b/Source/OpenTK/Platform/SDL2/Sdl2.cs
@@ -334,6 +334,7 @@ namespace OpenTK.Platform.SDL2
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetAttribute", ExactSpelling = true)]
public static extern int GetAttribute(ContextAttribute attr, out int value);
+ [SuppressUnmanagedCodeSecurity]
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetCurrentContext", ExactSpelling = true)]
public static extern IntPtr GetCurrentContext();
diff --git a/Source/OpenTK/Platform/Utilities.cs b/Source/OpenTK/Platform/Utilities.cs
index 4ed38158..67c92a78 100644
--- a/Source/OpenTK/Platform/Utilities.cs
+++ b/Source/OpenTK/Platform/Utilities.cs
@@ -170,6 +170,32 @@ namespace OpenTK.Platform
#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 ---
///