Skip to content

Commit

Permalink
Merge pull request opentk#153 from thefiddler/xwindowfix
Browse files Browse the repository at this point in the history
[X11] INativeWindow fixes
  • Loading branch information
thefiddler committed Jul 20, 2014
2 parents 17aa805 + 0f1776b commit 358b4dd
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 211 deletions.
19 changes: 18 additions & 1 deletion Source/GLControl/X11GLControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,22 @@ internal X11GLControl(GraphicsMode mode, Control control)
if (control == null)
throw new ArgumentNullException("control");

this.mode = mode;
// Note: the X11 window is created with a default XVisualInfo,
// that is not necessarily compatible with the desired GraphicsMode.
// This manifests in Nvidia binary drivers that fail in Glx.MakeCurrent()
// when GraphicsMode has a 32bpp color format.
// To work around this issue, we implicitly select a 24bpp color format when 32bpp is
// requested - this appears to work correctly in all cases.
// (The loss of the alpha channel does not matter, since WinForms do not support
// translucent windows on X11 in the first place.)
this.mode = new GraphicsMode(
new ColorFormat(mode.ColorFormat.Red, mode.ColorFormat.Green, mode.ColorFormat.Blue, 0),
mode.Depth,
mode.Stencil,
mode.Samples,
mode.AccumulatorFormat,
mode.Buffers,
mode.Stereo);

if (xplatui == null) throw new PlatformNotSupportedException(
"System.Windows.Forms.XplatUIX11 missing. Unsupported platform or Mono runtime version, aborting.");
Expand Down Expand Up @@ -105,6 +120,8 @@ public IGraphicsContext CreateContext(int major, int minor, GraphicsContextFlags
info = (XVisualInfo)Marshal.PtrToStructure(infoPtr, typeof(XVisualInfo));

// set the X11 colormap.
// Note: this only affects windows created in the future
// (do we even need this here?)
SetStaticFieldValue(xplatui, "CustomVisual", info.Visual);
SetStaticFieldValue(xplatui, "CustomColormap", XCreateColormap(display, rootWindow, info.Visual, 0));

Expand Down
6 changes: 1 addition & 5 deletions Source/OpenTK/Platform/Utilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -248,11 +248,7 @@ public static IWindowInfo CreateX11WindowInfo(IntPtr display, int screen, IntPtr
window.Screen = screen;
window.Handle = windowHandle;
window.RootWindow = rootWindow;
if (visualInfo != IntPtr.Zero)
{
window.VisualInfo = (X11.XVisualInfo)Marshal.PtrToStructure(visualInfo, typeof(X11.XVisualInfo));
}

window.Visual = visualInfo;
return window;
}

Expand Down
195 changes: 91 additions & 104 deletions Source/OpenTK/Platform/X11/X11GLContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,26 @@ public X11GLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shar
}
}

Mode = ModeSelector.SelectGraphicsMode(
mode.ColorFormat, mode.Depth, mode.Stencil, mode.Samples,
mode.AccumulatorFormat, mode.Buffers, mode.Stereo);
IntPtr visual = IntPtr.Zero;
IntPtr fbconfig = IntPtr.Zero;

// Once a window has a visual, we cannot use a different
// visual on the OpenGL context, or glXMakeCurrent might fail.
// Note: we should only check X11WindowInfo.Visual, as that
// is the only property that can be set by Utilities.CreateX11WindowInfo.
currentWindow = (X11WindowInfo)window;
currentWindow.VisualInfo = SelectVisual(Mode, currentWindow);

if (currentWindow.Visual != IntPtr.Zero)
{
visual = currentWindow.Visual;
fbconfig = currentWindow.FBConfig;
Mode = currentWindow.GraphicsMode;
}

if (Mode == null || !Mode.Index.HasValue)
{
Mode = ModeSelector.SelectGraphicsMode(mode, out visual, out fbconfig);
}

ContextHandle shareHandle = shared != null ?
(shared as IGraphicsContextInternal).Context : (ContextHandle)IntPtr.Zero;

Expand All @@ -99,84 +112,15 @@ public X11GLContext(GraphicsMode mode, IWindowInfo window, IGraphicsContext shar
// HACK: It seems that Catalyst 9.1 - 9.4 on Linux have problems with contexts created through
// GLX_ARB_create_context, including hideous input lag, no vsync and other madness.
// Use legacy context creation if the user doesn't request a 3.0+ context.
if ((major * 10 + minor >= 30) && SupportsCreateContextAttribs(Display, currentWindow))
if (fbconfig != IntPtr.Zero && (major * 10 + minor >= 30) && SupportsCreateContextAttribs(Display, currentWindow))
{
Debug.Write("Using GLX_ARB_create_context... ");

unsafe
{
// We need the FB config for the current GraphicsMode.
int count;
IntPtr* fbconfigs = Glx.ChooseFBConfig(Display, currentWindow.Screen,
new int[] {
(int)GLXAttribute.VISUAL_ID,
(int)Mode.Index,
0
}, out count);

if (count > 0)
{
List<int> attributes = new List<int>();
attributes.Add((int)ArbCreateContext.MajorVersion);
attributes.Add(major);
attributes.Add((int)ArbCreateContext.MinorVersion);
attributes.Add(minor);
if (flags != 0)
{
attributes.Add((int)ArbCreateContext.Flags);
attributes.Add((int)GetARBContextFlags(flags));
attributes.Add((int)ArbCreateContext.ProfileMask);
attributes.Add((int)GetARBProfileFlags(flags));
}
// According to the docs, " <attribList> specifies a list of attributes for the context.
// The list consists of a sequence of <name,value> pairs terminated by the
// value 0. [...]"
// Is this a single 0, or a <0, 0> pair? (Defensive coding: add two zeroes just in case).
attributes.Add(0);
attributes.Add(0);

using (new XLock(Display))
{
Handle = new ContextHandle(Glx.Arb.CreateContextAttribs(Display, *fbconfigs,
shareHandle.Handle, direct, attributes.ToArray()));

if (Handle == ContextHandle.Zero)
{
Debug.Write(String.Format("failed. Trying direct: {0}... ", !direct));
Handle = new ContextHandle(Glx.Arb.CreateContextAttribs(Display, *fbconfigs,
shareHandle.Handle, !direct, attributes.ToArray()));
}
}

if (Handle == ContextHandle.Zero)
Debug.WriteLine("failed.");
else
Debug.WriteLine("success!");

using (new XLock(Display))
{
Functions.XFree((IntPtr)fbconfigs);
}
}
}
Handle = CreateContextAttribs(Display, currentWindow.Screen,
fbconfig, direct, major, minor, flags, shareHandle);
}

if (Handle == ContextHandle.Zero)
{
Debug.Write("Using legacy context creation... ");

XVisualInfo info = currentWindow.VisualInfo;
using (new XLock(Display))
{
// Cannot pass a Property by reference.
Handle = new ContextHandle(Glx.CreateContext(Display, ref info, shareHandle.Handle, direct));

if (Handle == ContextHandle.Zero)
{
Debug.WriteLine(String.Format("failed. Trying direct: {0}... ", !direct));
Handle = new ContextHandle(Glx.CreateContext(Display, ref info, IntPtr.Zero, !direct));
}
}
Handle = CreateContextLegacy(Display, visual, direct, shareHandle);
}

if (Handle != ContextHandle.Zero)
Expand Down Expand Up @@ -208,6 +152,73 @@ public X11GLContext(ContextHandle handle, IWindowInfo window, IGraphicsContext s

#region --- Private Methods ---

static ContextHandle CreateContextAttribs(
IntPtr display, int screen, IntPtr fbconfig,
bool direct, int major, int minor,
GraphicsContextFlags flags, ContextHandle shareContext)
{
Debug.Write("Using GLX_ARB_create_context... ");
IntPtr context = IntPtr.Zero;

{
// We need the FB config for the current GraphicsMode.
List<int> attributes = new List<int>();
attributes.Add((int)ArbCreateContext.MajorVersion);
attributes.Add(major);
attributes.Add((int)ArbCreateContext.MinorVersion);
attributes.Add(minor);
if (flags != 0)
{
attributes.Add((int)ArbCreateContext.Flags);
attributes.Add((int)GetARBContextFlags(flags));
attributes.Add((int)ArbCreateContext.ProfileMask);
attributes.Add((int)GetARBProfileFlags(flags));
}
// According to the docs, " <attribList> specifies a list of attributes for the context.
// The list consists of a sequence of <name,value> pairs terminated by the
// value 0. [...]"
// Is this a single 0, or a <0, 0> pair? (Defensive coding: add two zeroes just in case).
attributes.Add(0);
attributes.Add(0);

using (new XLock(display))
{
context = Glx.Arb.CreateContextAttribs(display, fbconfig, shareContext.Handle, direct, attributes.ToArray());
if (context == IntPtr.Zero)
{
Debug.Write(String.Format("failed. Trying direct: {0}... ", !direct));
context = Glx.Arb.CreateContextAttribs(display, fbconfig, shareContext.Handle, !direct, attributes.ToArray());
}
}

if (context == IntPtr.Zero)
Debug.WriteLine("failed.");
else
Debug.WriteLine("success!");
}

return new ContextHandle(context);
}

static ContextHandle CreateContextLegacy(IntPtr display,
IntPtr info, bool direct, ContextHandle shareContext)
{
Debug.Write("Using legacy context creation... ");
IntPtr context;

using (new XLock(display))
{
context = Glx.CreateContext(display, info, shareContext.Handle, direct);
if (context == IntPtr.Zero)
{
Debug.WriteLine(String.Format("failed. Trying direct: {0}... ", !direct));
context = Glx.CreateContext(display, info, shareContext.Handle, !direct);
}
}

return new ContextHandle(context);
}

IntPtr Display
{
get { return display; }
Expand All @@ -221,38 +232,14 @@ IntPtr Display
}
}

#region XVisualInfo SelectVisual(GraphicsMode mode, X11WindowInfo currentWindow)

XVisualInfo SelectVisual(GraphicsMode mode, X11WindowInfo currentWindow)
{
XVisualInfo info = new XVisualInfo();
info.VisualID = (IntPtr)mode.Index;
info.Screen = currentWindow.Screen;
int items;

lock (API.Lock)
{
IntPtr vs = Functions.XGetVisualInfo(Display, XVisualInfoMask.ID | XVisualInfoMask.Screen, ref info, out items);
if (items == 0)
throw new GraphicsModeException(String.Format("Invalid GraphicsMode specified ({0}).", mode));

info = (XVisualInfo)Marshal.PtrToStructure(vs, typeof(XVisualInfo));
Functions.XFree(vs);
}

return info;
}

#endregion

ArbCreateContext GetARBContextFlags(GraphicsContextFlags flags)
static ArbCreateContext GetARBContextFlags(GraphicsContextFlags flags)
{
ArbCreateContext result = 0;
result |= (flags & GraphicsContextFlags.Debug) != 0 ? ArbCreateContext.DebugBit : 0;
return result;
}

ArbCreateContext GetARBProfileFlags(GraphicsContextFlags flags)
static ArbCreateContext GetARBProfileFlags(GraphicsContextFlags flags)
{
ArbCreateContext result = 0;
result |= (flags & GraphicsContextFlags.ForwardCompatible) != 0 ?
Expand Down
30 changes: 15 additions & 15 deletions Source/OpenTK/Platform/X11/X11GLNative.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ internal sealed class X11GLNative : NativeWindowBase
#region Constructors

public X11GLNative(int x, int y, int width, int height, string title,
GraphicsMode mode,GameWindowFlags options, DisplayDevice device)
GraphicsMode mode, GameWindowFlags options, DisplayDevice device)
: this()
{
if (width <= 0)
Expand All @@ -154,17 +154,13 @@ public X11GLNative(int x, int y, int width, int height, string title,

using (new XLock(window.Display))
{
if (!mode.Index.HasValue)
{
mode = new X11GraphicsMode().SelectGraphicsMode(
mode.ColorFormat, mode.Depth, mode.Stencil, mode.Samples,
mode.AccumulatorFormat, mode.Buffers, mode.Stereo);
}
IntPtr visual;
IntPtr fbconfig;
window.GraphicsMode = new X11GraphicsMode()
.SelectGraphicsMode(mode, out visual, out fbconfig);

info.VisualID = mode.Index.Value;
int dummy;
window.VisualInfo = (XVisualInfo)Marshal.PtrToStructure(
Functions.XGetVisualInfo(window.Display, XVisualInfoMask.ID, ref info, out dummy), typeof(XVisualInfo));
window.Visual = visual;
window.FBConfig = fbconfig;

// Create a window on this display using the visual above
Debug.Write("Opening render window... ");
Expand Down Expand Up @@ -825,7 +821,7 @@ public override void ProcessEvents()
case XEventName.ClientMessage:
if (!isExiting && e.ClientMessageEvent.ptr1 == _atom_wm_destroy)
{
Debug.WriteLine("Exit message received.");
Debug.Print("[X11] Exit message received for window {0:X} on display {1:X}", window.Handle, window.Display);
CancelEventArgs ce = new CancelEventArgs();
OnClosing(ce);

Expand Down Expand Up @@ -892,7 +888,7 @@ public override void ProcessEvents()
int x = e.MotionEvent.x;
int y = e.MotionEvent.y;

if (x != 0 || y != 0)
if (x != MouseState.X || y != MouseState.Y)
{
OnMouseMove(
MathHelper.Clamp(x, 0, Width),
Expand Down Expand Up @@ -1624,6 +1620,8 @@ public override IWindowInfo WindowInfo

public void Exit()
{
Debug.Print("[X11] Sending exit message window {0:X} on display {1:X}", window.Handle, window.Display);

XEvent ev = new XEvent();
ev.type = XEventName.ClientMessage;
ev.ClientMessageEvent.format = 32;
Expand All @@ -1644,10 +1642,12 @@ public void Exit()

public void DestroyWindow()
{
Debug.WriteLine("X11GLNative shutdown sequence initiated.");
Debug.Print("[X11] Destroying window {0:X} on display {1:X}", window.Handle, window.Display);

using (new XLock(window.Display))
{
Functions.XSync(window.Display, true);
Functions.XUnmapWindow(window.Display, window.Handle);
Functions.XSync(window.Display, false);
Functions.XDestroyWindow(window.Display, window.Handle);
exists = false;
}
Expand Down
Loading

0 comments on commit 358b4dd

Please sign in to comment.