diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props
index a43c99e978..cf8e0fd13a 100644
--- a/build/SkiaSharp.props
+++ b/build/SkiaSharp.props
@@ -1,6 +1,6 @@
-
+
diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
index 1f8e991d2e..18cef7d64e 100644
--- a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
+++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
@@ -287,6 +287,13 @@ namespace Avalonia.Controls
ValueProperty.Changed.Subscribe(OnValueChanged);
}
+ ///
+ protected override void OnLostFocus(RoutedEventArgs e)
+ {
+ CommitInput();
+ base.OnLostFocus(e);
+ }
+
///
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
{
diff --git a/src/Avalonia.OpenGL/EglDisplay.cs b/src/Avalonia.OpenGL/EglDisplay.cs
index 2a75e9458e..b14932acfe 100644
--- a/src/Avalonia.OpenGL/EglDisplay.cs
+++ b/src/Avalonia.OpenGL/EglDisplay.cs
@@ -86,18 +86,8 @@ namespace Avalonia.OpenGL
if (_contextAttributes == null)
throw new OpenGlException("No suitable EGL config was found");
-
- GlInterface = new GlInterface((proc, optional) =>
- {
- using (var u = new Utf8Buffer(proc))
- {
- var rv = _egl.GetProcAddress(u);
- if (rv == IntPtr.Zero && !optional)
- throw new OpenGlException("Missing function " + proc);
- return rv;
- }
- });
+ GlInterface = GlInterface.FromNativeUtf8GetProcAddress(b => _egl.GetProcAddress(b));
}
public EglDisplay() : this(new EglInterface())
diff --git a/src/Avalonia.OpenGL/EglInterface.cs b/src/Avalonia.OpenGL/EglInterface.cs
index c41a01340c..00fcd97af0 100644
--- a/src/Avalonia.OpenGL/EglInterface.cs
+++ b/src/Avalonia.OpenGL/EglInterface.cs
@@ -34,61 +34,61 @@ namespace Avalonia.OpenGL
// ReSharper disable UnassignedGetOnlyAutoProperty
public delegate int EglGetError();
- [EntryPoint("eglGetError")]
+ [GlEntryPoint("eglGetError")]
public EglGetError GetError { get; }
public delegate IntPtr EglGetDisplay(IntPtr nativeDisplay);
- [EntryPoint("eglGetDisplay")]
+ [GlEntryPoint("eglGetDisplay")]
public EglGetDisplay GetDisplay { get; }
public delegate IntPtr EglGetPlatformDisplayEXT(int platform, IntPtr nativeDisplay, int[] attrs);
- [EntryPoint("eglGetPlatformDisplayEXT", true)]
+ [GlEntryPoint("eglGetPlatformDisplayEXT", true)]
public EglGetPlatformDisplayEXT GetPlatformDisplayEXT { get; }
public delegate bool EglInitialize(IntPtr display, out int major, out int minor);
- [EntryPoint("eglInitialize")]
+ [GlEntryPoint("eglInitialize")]
public EglInitialize Initialize { get; }
public delegate IntPtr EglGetProcAddress(Utf8Buffer proc);
- [EntryPoint("eglGetProcAddress")]
+ [GlEntryPoint("eglGetProcAddress")]
public EglGetProcAddress GetProcAddress { get; }
public delegate bool EglBindApi(int api);
- [EntryPoint("eglBindAPI")]
+ [GlEntryPoint("eglBindAPI")]
public EglBindApi BindApi { get; }
public delegate bool EglChooseConfig(IntPtr display, int[] attribs,
out IntPtr surfaceConfig, int numConfigs, out int choosenConfig);
- [EntryPoint("eglChooseConfig")]
+ [GlEntryPoint("eglChooseConfig")]
public EglChooseConfig ChooseConfig { get; }
public delegate IntPtr EglCreateContext(IntPtr display, IntPtr config,
IntPtr share, int[] attrs);
- [EntryPoint("eglCreateContext")]
+ [GlEntryPoint("eglCreateContext")]
public EglCreateContext CreateContext { get; }
public delegate IntPtr EglCreatePBufferSurface(IntPtr display, IntPtr config, int[] attrs);
- [EntryPoint("eglCreatePbufferSurface")]
+ [GlEntryPoint("eglCreatePbufferSurface")]
public EglCreatePBufferSurface CreatePBufferSurface { get; }
public delegate bool EglMakeCurrent(IntPtr display, IntPtr draw, IntPtr read, IntPtr context);
- [EntryPoint("eglMakeCurrent")]
+ [GlEntryPoint("eglMakeCurrent")]
public EglMakeCurrent MakeCurrent { get; }
public delegate void EglDisplaySurfaceVoidDelegate(IntPtr display, IntPtr surface);
- [EntryPoint("eglDestroySurface")]
+ [GlEntryPoint("eglDestroySurface")]
public EglDisplaySurfaceVoidDelegate DestroySurface { get; }
- [EntryPoint("eglSwapBuffers")]
+ [GlEntryPoint("eglSwapBuffers")]
public EglDisplaySurfaceVoidDelegate SwapBuffers { get; }
public delegate IntPtr
EglCreateWindowSurface(IntPtr display, IntPtr config, IntPtr window, int[] attrs);
- [EntryPoint("eglCreateWindowSurface")]
+ [GlEntryPoint("eglCreateWindowSurface")]
public EglCreateWindowSurface CreateWindowSurface { get; }
public delegate bool EglGetConfigAttrib(IntPtr display, IntPtr config, int attr, out int rv);
- [EntryPoint("eglGetConfigAttrib")]
+ [GlEntryPoint("eglGetConfigAttrib")]
public EglGetConfigAttrib GetConfigAttrib { get; }
// ReSharper restore UnassignedGetOnlyAutoProperty
diff --git a/src/Avalonia.OpenGL/EntryPointAttribute.cs b/src/Avalonia.OpenGL/GlEntryPointAttribute.cs
similarity index 56%
rename from src/Avalonia.OpenGL/EntryPointAttribute.cs
rename to src/Avalonia.OpenGL/GlEntryPointAttribute.cs
index 241f517df9..016c3d0af9 100644
--- a/src/Avalonia.OpenGL/EntryPointAttribute.cs
+++ b/src/Avalonia.OpenGL/GlEntryPointAttribute.cs
@@ -2,12 +2,13 @@ using System;
namespace Avalonia.OpenGL
{
- class EntryPointAttribute : Attribute
+ [AttributeUsage(AttributeTargets.Property)]
+ public class GlEntryPointAttribute : Attribute
{
public string EntryPoint { get; }
public bool Optional { get; }
- public EntryPointAttribute(string entryPoint, bool optional = false)
+ public GlEntryPointAttribute(string entryPoint, bool optional = false)
{
EntryPoint = entryPoint;
Optional = optional;
diff --git a/src/Avalonia.OpenGL/GlInterface.cs b/src/Avalonia.OpenGL/GlInterface.cs
index f23e1c5829..718afc4a94 100644
--- a/src/Avalonia.OpenGL/GlInterface.cs
+++ b/src/Avalonia.OpenGL/GlInterface.cs
@@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
+using Avalonia.Platform.Interop;
namespace Avalonia.OpenGL
{
@@ -7,43 +8,56 @@ namespace Avalonia.OpenGL
public class GlInterface : GlInterfaceBase
{
- private readonly Func _getProcAddress;
+ public string Version { get; }
public GlInterface(Func getProcAddress) : base(getProcAddress)
{
- _getProcAddress = getProcAddress;
+ var versionPtr = GetString(GlConsts.GL_VERSION);
+ if (versionPtr != IntPtr.Zero)
+ Version = Marshal.PtrToStringAnsi(versionPtr);
}
- public IntPtr GetProcAddress(string proc) => _getProcAddress(proc, true);
+ public GlInterface(Func n) : this(ConvertNative(n))
+ {
+
+ }
+
+ public static GlInterface FromNativeUtf8GetProcAddress(Func getProcAddress) =>
+ new GlInterface(getProcAddress);
+
public T GetProcAddress(string proc) => Marshal.GetDelegateForFunctionPointer(GetProcAddress(proc));
// ReSharper disable UnassignedGetOnlyAutoProperty
public delegate int GlGetError();
- [EntryPoint("glGetError")]
+ [GlEntryPoint("glGetError")]
public GlGetError GetError { get; }
public delegate void GlClearStencil(int s);
- [EntryPoint("glClearStencil")]
+ [GlEntryPoint("glClearStencil")]
public GlClearStencil ClearStencil { get; }
public delegate void GlClearColor(int r, int g, int b, int a);
- [EntryPoint("glClearColor")]
+ [GlEntryPoint("glClearColor")]
public GlClearColor ClearColor { get; }
public delegate void GlClear(int bits);
- [EntryPoint("glClear")]
+ [GlEntryPoint("glClear")]
public GlClear Clear { get; }
public delegate void GlViewport(int x, int y, int width, int height);
- [EntryPoint("glViewport")]
+ [GlEntryPoint("glViewport")]
public GlViewport Viewport { get; }
- [EntryPoint("glFlush")]
+ [GlEntryPoint("glFlush")]
public Action Flush { get; }
+ public delegate IntPtr GlGetString(int v);
+ [GlEntryPoint("glGetString")]
+ public GlGetString GetString { get; }
+
public delegate void GlGetIntegerv(int name, out int rv);
- [EntryPoint("glGetIntegerv")]
+ [GlEntryPoint("glGetIntegerv")]
public GlGetIntegerv GetIntegerv { get; }
// ReSharper restore UnassignedGetOnlyAutoProperty
diff --git a/src/Avalonia.OpenGL/GlInterfaceBase.cs b/src/Avalonia.OpenGL/GlInterfaceBase.cs
index 33191a6567..89ec0e77d4 100644
--- a/src/Avalonia.OpenGL/GlInterfaceBase.cs
+++ b/src/Avalonia.OpenGL/GlInterfaceBase.cs
@@ -1,16 +1,20 @@
using System;
using System.Reflection;
using System.Runtime.InteropServices;
+using Avalonia.Platform.Interop;
namespace Avalonia.OpenGL
{
public class GlInterfaceBase
{
+
+ private readonly Func _getProcAddress;
public GlInterfaceBase(Func getProcAddress)
{
+ _getProcAddress = getProcAddress;
foreach (var prop in this.GetType().GetProperties())
{
- var a = prop.GetCustomAttribute();
+ var a = prop.GetCustomAttribute();
if (a != null)
{
var fieldName = $"<{prop.Name}>k__BackingField";
@@ -24,5 +28,26 @@ namespace Avalonia.OpenGL
}
}
}
+
+ protected static Func ConvertNative(Func func) =>
+ (proc, optional) =>
+ {
+ using (var u = new Utf8Buffer(proc))
+ {
+ var rv = func(u);
+ if (rv == IntPtr.Zero && !optional)
+ throw new OpenGlException("Missing function " + proc);
+ return rv;
+ }
+ };
+
+ public GlInterfaceBase(Func nativeGetProcAddress) : this(ConvertNative(nativeGetProcAddress))
+ {
+
+ }
+
+ public IntPtr GetProcAddress(string proc) => _getProcAddress(proc, true);
+ public IntPtr GetProcAddress(string proc, bool optional) => _getProcAddress(proc, optional);
+
}
}
diff --git a/src/Avalonia.ReactiveUI/ReactiveUserControl.cs b/src/Avalonia.ReactiveUI/ReactiveUserControl.cs
index 04c6f100a6..43e2ef93b6 100644
--- a/src/Avalonia.ReactiveUI/ReactiveUserControl.cs
+++ b/src/Avalonia.ReactiveUI/ReactiveUserControl.cs
@@ -16,7 +16,7 @@ namespace Avalonia
public class ReactiveUserControl : UserControl, IViewFor where TViewModel : class
{
public static readonly AvaloniaProperty ViewModelProperty = AvaloniaProperty
- .Register, TViewModel>(nameof(ViewModel));
+ .Register, TViewModel>(nameof(ViewModel));
///
/// Initializes a new instance of the class.
@@ -41,4 +41,4 @@ namespace Avalonia
set => ViewModel = (TViewModel)value;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.X11/Glx/Glx.cs b/src/Avalonia.X11/Glx/Glx.cs
new file mode 100644
index 0000000000..c3a2fd2050
--- /dev/null
+++ b/src/Avalonia.X11/Glx/Glx.cs
@@ -0,0 +1,91 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using Avalonia.OpenGL;
+using Avalonia.Platform.Interop;
+// ReSharper disable UnassignedGetOnlyAutoProperty
+
+namespace Avalonia.X11.Glx
+{
+ unsafe class GlxInterface : GlInterfaceBase
+ {
+ private const string libGL = "libGL.so.1";
+ [GlEntryPointAttribute("glXMakeContextCurrent")]
+ public GlxMakeContextCurrent MakeContextCurrent { get; }
+ public delegate bool GlxMakeContextCurrent(IntPtr display, IntPtr draw, IntPtr read, IntPtr context);
+
+ [GlEntryPoint("glXCreatePbuffer")]
+ public GlxCreatePbuffer CreatePbuffer { get; }
+
+ public delegate IntPtr GlxCreatePbuffer(IntPtr dpy, IntPtr fbc, int[] attrib_list);
+
+ [GlEntryPointAttribute("glXChooseVisual")]
+ public GlxChooseVisual ChooseVisual { get; }
+ public delegate XVisualInfo* GlxChooseVisual(IntPtr dpy, int screen, int[] attribList);
+
+
+ [GlEntryPointAttribute("glXCreateContext")]
+ public GlxCreateContext CreateContext { get; }
+ public delegate IntPtr GlxCreateContext(IntPtr dpy, XVisualInfo* vis, IntPtr shareList, bool direct);
+
+
+ [GlEntryPointAttribute("glXCreateContextAttribsARB")]
+ public GlxCreateContextAttribsARB CreateContextAttribsARB { get; }
+ public delegate IntPtr GlxCreateContextAttribsARB(IntPtr dpy, IntPtr fbconfig, IntPtr shareList,
+ bool direct, int[] attribs);
+
+
+ [DllImport(libGL, EntryPoint = "glXGetProcAddress")]
+ public static extern IntPtr GlxGetProcAddress(Utf8Buffer buffer);
+
+
+ [GlEntryPointAttribute("glXDestroyContext")]
+ public GlxDestroyContext DestroyContext { get; }
+ public delegate void GlxDestroyContext(IntPtr dpy, IntPtr ctx);
+
+
+ [GlEntryPointAttribute("glXChooseFBConfig")]
+ public GlxChooseFBConfig ChooseFBConfig { get; }
+ public delegate IntPtr* GlxChooseFBConfig(IntPtr dpy, int screen, int[] attrib_list, out int nelements);
+
+
+ public IntPtr* GlxChooseFbConfig(IntPtr dpy, int screen, IEnumerable attribs, out int nelements)
+ {
+ var arr = attribs.Concat(new[]{0}).ToArray();
+ return ChooseFBConfig(dpy, screen, arr, out nelements);
+ }
+
+ [GlEntryPointAttribute("glXGetVisualFromFBConfig")]
+ public GlxGetVisualFromFBConfig GetVisualFromFBConfig { get; }
+ public delegate XVisualInfo * GlxGetVisualFromFBConfig(IntPtr dpy, IntPtr config);
+
+
+ [GlEntryPointAttribute("glXGetFBConfigAttrib")]
+ public GlxGetFBConfigAttrib GetFBConfigAttrib { get; }
+ public delegate int GlxGetFBConfigAttrib(IntPtr dpy, IntPtr config, int attribute, out int value);
+
+
+ [GlEntryPointAttribute("glXSwapBuffers")]
+ public GlxSwapBuffers SwapBuffers { get; }
+ public delegate void GlxSwapBuffers(IntPtr dpy, IntPtr drawable);
+
+
+ [GlEntryPointAttribute("glXWaitX")]
+ public GlxWaitX WaitX { get; }
+ public delegate void GlxWaitX();
+
+
+ [GlEntryPointAttribute("glXWaitGL")]
+ public GlxWaitGL WaitGL { get; }
+ public delegate void GlxWaitGL();
+
+ public delegate int GlGetError();
+ [GlEntryPoint("glGetError")]
+ public GlGetError GetError { get; }
+
+ public GlxInterface() : base(GlxGetProcAddress)
+ {
+ }
+ }
+}
diff --git a/src/Avalonia.X11/Glx/GlxConsts.cs b/src/Avalonia.X11/Glx/GlxConsts.cs
new file mode 100644
index 0000000000..65f253696a
--- /dev/null
+++ b/src/Avalonia.X11/Glx/GlxConsts.cs
@@ -0,0 +1,106 @@
+// ReSharper disable InconsistentNaming
+// ReSharper disable IdentifierTypo
+// ReSharper disable UnusedMember.Global
+#pragma warning disable 414
+namespace Avalonia.X11.Glx
+{
+ class GlxConsts
+ {
+ public const int GLX_USE_GL = 1;
+ public const int GLX_BUFFER_SIZE = 2;
+ public const int GLX_LEVEL = 3;
+ public const int GLX_RGBA = 4;
+ public const int GLX_DOUBLEBUFFER = 5;
+ public const int GLX_STEREO = 6;
+ public const int GLX_AUX_BUFFERS = 7;
+ public const int GLX_RED_SIZE = 8;
+ public const int GLX_GREEN_SIZE = 9;
+ public const int GLX_BLUE_SIZE = 10;
+ public const int GLX_ALPHA_SIZE = 11;
+ public const int GLX_DEPTH_SIZE = 12;
+ public const int GLX_STENCIL_SIZE = 13;
+ public const int GLX_ACCUM_RED_SIZE = 14;
+ public const int GLX_ACCUM_GREEN_SIZE = 15;
+ public const int GLX_ACCUM_BLUE_SIZE = 16;
+ public const int GLX_ACCUM_ALPHA_SIZE = 17;
+ public const int GLX_BAD_SCREEN = 1;
+ public const int GLX_BAD_ATTRIBUTE = 2;
+ public const int GLX_NO_EXTENSION = 3;
+ public const int GLX_BAD_VISUAL = 4;
+ public const int GLX_BAD_CONTEXT = 5;
+ public const int GLX_BAD_VALUE = 6;
+ public const int GLX_BAD_ENUM = 7;
+ public const int GLX_VENDOR = 1;
+ public const int GLX_VERSION = 2;
+ public const int GLX_EXTENSIONS= 3;
+ public const int GLX_CONFIG_CAVEAT = 0x20;
+ public const int GLX_DONT_CARE = unchecked((int)0xFFFFFFFF);
+ public const int GLX_X_VISUAL_TYPE = 0x22;
+ public const int GLX_TRANSPARENT_TYPE = 0x23;
+ public const int GLX_TRANSPARENT_INDEX_VALUE = 0x24;
+ public const int GLX_TRANSPARENT_RED_VALUE = 0x25;
+ public const int GLX_TRANSPARENT_GREEN_VALUE = 0x26;
+ public const int GLX_TRANSPARENT_BLUE_VALUE = 0x27;
+ public const int GLX_TRANSPARENT_ALPHA_VALUE = 0x28;
+ public const int GLX_WINDOW_BIT = 0x00000001;
+ public const int GLX_PIXMAP_BIT = 0x00000002;
+ public const int GLX_PBUFFER_BIT = 0x00000004;
+ public const int GLX_AUX_BUFFERS_BIT = 0x00000010;
+ public const int GLX_FRONT_LEFT_BUFFER_BIT = 0x00000001;
+ public const int GLX_FRONT_RIGHT_BUFFER_BIT = 0x00000002;
+ public const int GLX_BACK_LEFT_BUFFER_BIT = 0x00000004;
+ public const int GLX_BACK_RIGHT_BUFFER_BIT = 0x00000008;
+ public const int GLX_DEPTH_BUFFER_BIT = 0x00000020;
+ public const int GLX_STENCIL_BUFFER_BIT = 0x00000040;
+ public const int GLX_ACCUM_BUFFER_BIT = 0x00000080;
+ public const int GLX_NONE = 0x8000;
+ public const int GLX_SLOW_CONFIG = 0x8001;
+ public const int GLX_TRUE_COLOR = 0x8002;
+ public const int GLX_DIRECT_COLOR = 0x8003;
+ public const int GLX_PSEUDO_COLOR = 0x8004;
+ public const int GLX_STATIC_COLOR = 0x8005;
+ public const int GLX_GRAY_SCALE = 0x8006;
+ public const int GLX_STATIC_GRAY = 0x8007;
+ public const int GLX_TRANSPARENT_RGB = 0x8008;
+ public const int GLX_TRANSPARENT_INDEX = 0x8009;
+ public const int GLX_VISUAL_ID = 0x800B;
+ public const int GLX_SCREEN = 0x800C;
+ public const int GLX_NON_CONFORMANT_CONFIG = 0x800D;
+ public const int GLX_DRAWABLE_TYPE = 0x8010;
+ public const int GLX_RENDER_TYPE = 0x8011;
+ public const int GLX_X_RENDERABLE = 0x8012;
+ public const int GLX_FBCONFIG_ID = 0x8013;
+ public const int GLX_RGBA_TYPE = 0x8014;
+ public const int GLX_COLOR_INDEX_TYPE = 0x8015;
+ public const int GLX_MAX_PBUFFER_WIDTH = 0x8016;
+ public const int GLX_MAX_PBUFFER_HEIGHT = 0x8017;
+ public const int GLX_MAX_PBUFFER_PIXELS = 0x8018;
+ public const int GLX_PRESERVED_CONTENTS = 0x801B;
+ public const int GLX_LARGEST_PBUFFER = 0x801C;
+ public const int GLX_WIDTH = 0x801D;
+ public const int GLX_HEIGHT = 0x801E;
+ public const int GLX_EVENT_MASK = 0x801F;
+ public const int GLX_DAMAGED = 0x8020;
+ public const int GLX_SAVED = 0x8021;
+ public const int GLX_WINDOW = 0x8022;
+ public const int GLX_PBUFFER = 0x8023;
+ public const int GLX_PBUFFER_HEIGHT = 0x8040;
+ public const int GLX_PBUFFER_WIDTH = 0x8041;
+ public const int GLX_RGBA_BIT = 0x00000001;
+ public const int GLX_COLOR_INDEX_BIT = 0x00000002;
+ public const int GLX_PBUFFER_CLOBBER_MASK = 0x08000000;
+ public const int GLX_SAMPLE_BUFFERS = 0x186a0 /*100000*/;
+ public const int GLX_SAMPLES = 0x186a1 /*100001*/;
+ public const int GLX_PbufferClobber = 0;
+ public const int GLX_BufferSwapComplete = 1;
+ public const int GLX_CONTEXT_DEBUG_BIT_ARB = 0x00000001;
+ public const int GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB = 0x00000002;
+ public const int GLX_CONTEXT_MAJOR_VERSION_ARB = 0x2091;
+ public const int GLX_CONTEXT_MINOR_VERSION_ARB = 0x2092;
+ public const int GLX_CONTEXT_FLAGS_ARB = 0x2094;
+ public const int GLX_CONTEXT_CORE_PROFILE_BIT_ARB = 0x00000001;
+ public const int GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB = 0x00000002;
+ public const int GLX_CONTEXT_PROFILE_MASK_ARB = 0x9126;
+
+ }
+}
diff --git a/src/Avalonia.X11/Glx/GlxContext.cs b/src/Avalonia.X11/Glx/GlxContext.cs
new file mode 100644
index 0000000000..dd95841d57
--- /dev/null
+++ b/src/Avalonia.X11/Glx/GlxContext.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Reactive.Disposables;
+using System.Threading;
+using Avalonia.OpenGL;
+namespace Avalonia.X11.Glx
+{
+ class GlxContext : IGlContext
+ {
+ public IntPtr Handle { get; }
+ public GlxInterface Glx { get; }
+ private readonly X11Info _x11;
+ private readonly IntPtr _defaultXid;
+ private readonly object _lock = new object();
+
+ public GlxContext(GlxInterface glx, IntPtr handle, GlxDisplay display, X11Info x11, IntPtr defaultXid)
+ {
+ Handle = handle;
+ Glx = glx;
+ _x11 = x11;
+ _defaultXid = defaultXid;
+ Display = display;
+ }
+
+ public GlxDisplay Display { get; }
+ IGlDisplay IGlContext.Display => Display;
+
+ public IDisposable Lock()
+ {
+ Monitor.Enter(_lock);
+ return Disposable.Create(() => Monitor.Exit(_lock));
+ }
+
+ public void MakeCurrent() => MakeCurrent(_defaultXid);
+
+ public void MakeCurrent(IntPtr xid)
+ {
+ if (!Glx.MakeContextCurrent(_x11.Display, xid, xid, Handle))
+ throw new OpenGlException("glXMakeContextCurrent failed ");
+ }
+ }
+}
diff --git a/src/Avalonia.X11/Glx/GlxDisplay.cs b/src/Avalonia.X11/Glx/GlxDisplay.cs
new file mode 100644
index 0000000000..5602b33280
--- /dev/null
+++ b/src/Avalonia.X11/Glx/GlxDisplay.cs
@@ -0,0 +1,136 @@
+using System;
+using System.Linq;
+using Avalonia.OpenGL;
+using static Avalonia.X11.Glx.GlxConsts;
+
+namespace Avalonia.X11.Glx
+{
+ unsafe class GlxDisplay : IGlDisplay
+ {
+ private readonly X11Info _x11;
+ private readonly IntPtr _fbconfig;
+ private readonly XVisualInfo* _visual;
+ public GlDisplayType Type => GlDisplayType.OpenGL2;
+ public GlInterface GlInterface { get; }
+
+ public XVisualInfo* VisualInfo => _visual;
+ public int SampleCount { get; }
+ public int StencilSize { get; }
+
+ public GlxContext ImmediateContext { get; }
+ public GlxContext DeferredContext { get; }
+ public GlxInterface Glx { get; } = new GlxInterface();
+ public GlxDisplay(X11Info x11)
+ {
+ _x11 = x11;
+
+ var baseAttribs = new[]
+ {
+ GLX_X_RENDERABLE, 1,
+ GLX_RENDER_TYPE, GLX_RGBA_BIT,
+ GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PBUFFER_BIT,
+ GLX_DOUBLEBUFFER, 1,
+ GLX_RED_SIZE, 8,
+ GLX_GREEN_SIZE, 8,
+ GLX_BLUE_SIZE, 8,
+ GLX_ALPHA_SIZE, 8,
+ GLX_DEPTH_SIZE, 1,
+ GLX_STENCIL_SIZE, 8,
+
+ };
+
+ foreach (var attribs in new[]
+ {
+ //baseAttribs.Concat(multiattribs),
+ baseAttribs,
+ })
+ {
+ var ptr = Glx.ChooseFBConfig(_x11.Display, x11.DefaultScreen,
+ attribs, out var count);
+ for (var c = 0 ; c < count; c++)
+ {
+
+ var visual = Glx.GetVisualFromFBConfig(_x11.Display, ptr[c]);
+ // We prefer 32 bit visuals
+ if (_fbconfig == IntPtr.Zero || visual->depth == 32)
+ {
+ _fbconfig = ptr[c];
+ _visual = visual;
+ if(visual->depth == 32)
+ break;
+ }
+ }
+
+ if (_fbconfig != IntPtr.Zero)
+ break;
+ }
+
+ if (_fbconfig == IntPtr.Zero)
+ throw new OpenGlException("Unable to choose FBConfig");
+
+ if (_visual == null)
+ throw new OpenGlException("Unable to get visual info from FBConfig");
+ if (Glx.GetFBConfigAttrib(_x11.Display, _fbconfig, GLX_SAMPLES, out var samples) == 0)
+ SampleCount = samples;
+ if (Glx.GetFBConfigAttrib(_x11.Display, _fbconfig, GLX_STENCIL_SIZE, out var stencil) == 0)
+ StencilSize = stencil;
+
+ var pbuffers = Enumerable.Range(0, 2).Select(_ => Glx.CreatePbuffer(_x11.Display, _fbconfig, new[]
+ {
+ GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, 0
+ })).ToList();
+
+ XLib.XFlush(_x11.Display);
+
+ ImmediateContext = CreateContext(pbuffers[0],null);
+ DeferredContext = CreateContext(pbuffers[1], ImmediateContext);
+ ImmediateContext.MakeCurrent();
+ var err = Glx.GetError();
+
+ GlInterface = new GlInterface(GlxInterface.GlxGetProcAddress);
+ if (GlInterface.Version == null)
+ throw new OpenGlException("GL version string is null, aborting");
+ }
+
+ public void ClearContext() => Glx.MakeContextCurrent(_x11.Display,
+ IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
+
+ public GlxContext CreateContext(IGlContext share) => CreateContext(IntPtr.Zero, share);
+ public GlxContext CreateContext(IntPtr defaultXid, IGlContext share)
+ {
+ var sharelist = ((GlxContext)share)?.Handle ?? IntPtr.Zero;
+ IntPtr handle = default;
+ foreach (var ver in new[]
+ {
+ new Version(4, 0), new Version(3, 2),
+ new Version(3, 0), new Version(2, 0)
+ })
+ {
+
+ var attrs = new[]
+ {
+ GLX_CONTEXT_MAJOR_VERSION_ARB, ver.Major,
+ GLX_CONTEXT_MINOR_VERSION_ARB, ver.Minor,
+ GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
+ 0
+ };
+ try
+ {
+ handle = Glx.CreateContextAttribsARB(_x11.Display, _fbconfig, sharelist, true, attrs);
+ if (handle != IntPtr.Zero)
+ break;
+ }
+ catch
+ {
+ break;
+ }
+ }
+
+ if (handle == IntPtr.Zero)
+ throw new OpenGlException("Unable to create direct GLX context");
+ return new GlxContext(new GlxInterface(), handle, this, _x11, defaultXid);
+ }
+
+ public void SwapBuffers(IntPtr xid) => Glx.SwapBuffers(_x11.Display, xid);
+ }
+}
diff --git a/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs b/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs
new file mode 100644
index 0000000000..45a46bd6f5
--- /dev/null
+++ b/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs
@@ -0,0 +1,86 @@
+using System;
+using Avalonia.OpenGL;
+
+namespace Avalonia.X11.Glx
+{
+ class GlxGlPlatformSurface: IGlPlatformSurface
+ {
+
+ private readonly GlxDisplay _display;
+ private readonly GlxContext _context;
+ private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info;
+
+ public GlxGlPlatformSurface(GlxDisplay display, GlxContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info)
+ {
+ _display = display;
+ _context = context;
+ _info = info;
+ }
+
+ public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
+ {
+ return new RenderTarget(_context, _info);
+ }
+
+ class RenderTarget : IGlPlatformSurfaceRenderTarget
+ {
+ private readonly GlxContext _context;
+ private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info;
+
+ public RenderTarget(GlxContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info)
+ {
+ _context = context;
+ _info = info;
+ }
+
+ public void Dispose()
+ {
+ // No-op
+ }
+
+ public IGlPlatformSurfaceRenderingSession BeginDraw()
+ {
+ var l = _context.Lock();
+ try
+ {
+ _context.MakeCurrent(_info.Handle);
+ return new Session(_context, _info, l);
+ }
+ catch
+ {
+ l.Dispose();
+ throw;
+ }
+ }
+
+ class Session : IGlPlatformSurfaceRenderingSession
+ {
+ private readonly GlxContext _context;
+ private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info;
+ private IDisposable _lock;
+
+ public Session(GlxContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info,
+ IDisposable @lock)
+ {
+ _context = context;
+ _info = info;
+ _lock = @lock;
+ }
+
+ public void Dispose()
+ {
+ _context.Display.GlInterface.Flush();
+ _context.Glx.WaitGL();
+ _context.Display.SwapBuffers(_info.Handle);
+ _context.Glx.WaitX();
+ _context.Display.ClearContext();
+ _lock.Dispose();
+ }
+
+ public IGlDisplay Display => _context.Display;
+ public PixelSize Size => _info.Size;
+ public double Scaling => _info.Scaling;
+ }
+ }
+ }
+}
diff --git a/src/Avalonia.X11/Glx/GlxPlatformFeature.cs b/src/Avalonia.X11/Glx/GlxPlatformFeature.cs
new file mode 100644
index 0000000000..d15b5fe4b8
--- /dev/null
+++ b/src/Avalonia.X11/Glx/GlxPlatformFeature.cs
@@ -0,0 +1,44 @@
+using System;
+using Avalonia.Logging;
+using Avalonia.OpenGL;
+
+namespace Avalonia.X11.Glx
+{
+ class GlxGlPlatformFeature : IWindowingPlatformGlFeature
+ {
+ public GlxDisplay Display { get; private set; }
+ public IGlContext ImmediateContext { get; private set; }
+ public GlxContext DeferredContext { get; private set; }
+
+ public static bool TryInitialize(X11Info x11)
+ {
+ var feature = TryCreate(x11);
+ if (feature != null)
+ {
+ AvaloniaLocator.CurrentMutable.Bind().ToConstant(feature);
+ return true;
+ }
+
+ return false;
+ }
+
+ public static GlxGlPlatformFeature TryCreate(X11Info x11)
+ {
+ try
+ {
+ var disp = new GlxDisplay(x11);
+ return new GlxGlPlatformFeature
+ {
+ Display = disp,
+ ImmediateContext = disp.ImmediateContext,
+ DeferredContext = disp.DeferredContext
+ };
+ }
+ catch(Exception e)
+ {
+ Logger.Error("OpenGL", null, "Unable to initialize GLX-based rendering: {0}", e);
+ return null;
+ }
+ }
+ }
+}
diff --git a/src/Avalonia.X11/X11FramebufferSurface.cs b/src/Avalonia.X11/X11FramebufferSurface.cs
index 70220cebcd..07c88b51a8 100644
--- a/src/Avalonia.X11/X11FramebufferSurface.cs
+++ b/src/Avalonia.X11/X11FramebufferSurface.cs
@@ -8,12 +8,14 @@ namespace Avalonia.X11
{
private readonly IntPtr _display;
private readonly IntPtr _xid;
+ private readonly int _depth;
private readonly Func _scaling;
- public X11FramebufferSurface(IntPtr display, IntPtr xid, Func scaling)
+ public X11FramebufferSurface(IntPtr display, IntPtr xid, int depth, Func scaling)
{
_display = display;
_xid = xid;
+ _depth = depth;
_scaling = scaling;
}
@@ -23,7 +25,7 @@ namespace Avalonia.X11
XGetGeometry(_display, _xid, out var root, out var x, out var y, out var width, out var height,
out var bw, out var d);
XUnlockDisplay(_display);
- return new X11Framebuffer(_display, _xid, 24,width, height, _scaling());
+ return new X11Framebuffer(_display, _xid, _depth, width, height, _scaling());
}
}
}
diff --git a/src/Avalonia.X11/X11Info.cs b/src/Avalonia.X11/X11Info.cs
index 6e4b31fb5c..8cfcbb9836 100644
--- a/src/Avalonia.X11/X11Info.cs
+++ b/src/Avalonia.X11/X11Info.cs
@@ -7,7 +7,7 @@ using static Avalonia.X11.XLib;
// ReSharper disable UnusedAutoPropertyAccessor.Local
namespace Avalonia.X11
{
- class X11Info
+ unsafe class X11Info
{
public IntPtr Display { get; }
public IntPtr DeferredDisplay { get; }
@@ -31,6 +31,7 @@ namespace Avalonia.X11
public Version XInputVersion { get; }
public IntPtr LastActivityTimestamp { get; set; }
+ public XVisualInfo? TransparentVisualInfo { get; set; }
public unsafe X11Info(IntPtr display, IntPtr deferredDisplay)
{
@@ -45,7 +46,10 @@ namespace Avalonia.X11
//TODO: Open an actual XIM once we get support for preedit in our textbox
XSetLocaleModifiers("@im=none");
Xim = XOpenIM(display, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
-
+ XMatchVisualInfo(Display, DefaultScreen, 32, 4, out var visual);
+ if (visual.depth == 32)
+ TransparentVisualInfo = visual;
+
try
{
if (XRRQueryExtension(display, out int randrEventBase, out var randrErrorBase) != 0)
diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs
index 3b50dff5c8..323411e319 100644
--- a/src/Avalonia.X11/X11Platform.cs
+++ b/src/Avalonia.X11/X11Platform.cs
@@ -9,6 +9,7 @@ using Avalonia.OpenGL;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.X11;
+using Avalonia.X11.Glx;
using static Avalonia.X11.XLib;
namespace Avalonia.X11
{
@@ -23,7 +24,7 @@ namespace Avalonia.X11
public X11Info Info { get; private set; }
public IX11Screens X11Screens { get; private set; }
public IScreenImpl Screens { get; private set; }
- public void Initialize()
+ public void Initialize(X11PlatformOptions options)
{
XInitThreads();
Display = XOpenDisplay(IntPtr.Zero);
@@ -54,8 +55,14 @@ namespace Avalonia.X11
if (xi2.Init(this))
XI2 = xi2;
}
- EglGlPlatformFeature.TryInitialize();
+ if (options.UseGpu)
+ {
+ if (options.UseEGL)
+ EglGlPlatformFeature.TryInitialize();
+ else
+ GlxGlPlatformFeature.TryInitialize(Info);
+ }
}
public IntPtr DeferredDisplay { get; set; }
@@ -79,15 +86,22 @@ namespace Avalonia.X11
namespace Avalonia
{
+
+ public class X11PlatformOptions
+ {
+ public bool UseEGL { get; set; }
+ public bool UseGpu { get; set; } = true;
+ }
public static class AvaloniaX11PlatformExtensions
{
- public static T UseX11(this T builder) where T : AppBuilderBase, new()
+ public static T UseX11(this T builder, X11PlatformOptions options = null) where T : AppBuilderBase, new()
{
- builder.UseWindowingSubsystem(() => new AvaloniaX11Platform().Initialize());
+ builder.UseWindowingSubsystem(() => new AvaloniaX11Platform().Initialize(options ?? new X11PlatformOptions()));
return builder;
}
- public static void InitializeX11Platform() => new AvaloniaX11Platform().Initialize();
+ public static void InitializeX11Platform(X11PlatformOptions options = null) =>
+ new AvaloniaX11Platform().Initialize(options ?? new X11PlatformOptions());
}
}
diff --git a/src/Avalonia.X11/X11PlatformThreading.cs b/src/Avalonia.X11/X11PlatformThreading.cs
index f66afc4a83..0215e43d98 100644
--- a/src/Avalonia.X11/X11PlatformThreading.cs
+++ b/src/Avalonia.X11/X11PlatformThreading.cs
@@ -164,39 +164,33 @@ namespace Avalonia.X11
void HandleX11(CancellationToken cancellationToken)
{
- while (true)
+ while (XPending(_display) != 0)
{
- var pending = XPending(_display);
- if (pending == 0)
- break;
- while (pending > 0)
+ if (cancellationToken.IsCancellationRequested)
+ return;
+ XNextEvent(_display, out var xev);
+ if (xev.type == XEventName.GenericEvent)
+ XGetEventData(_display, &xev.GenericEventCookie);
+ try
{
- if (cancellationToken.IsCancellationRequested)
- return;
- XNextEvent(_display, out var xev);
if (xev.type == XEventName.GenericEvent)
- XGetEventData(_display, &xev.GenericEventCookie);
- pending--;
- try
{
- if (xev.type == XEventName.GenericEvent)
+ if (_platform.XI2 != null && _platform.Info.XInputOpcode ==
+ xev.GenericEventCookie.extension)
{
- if (_platform.XI2 != null && _platform.Info.XInputOpcode ==
- xev.GenericEventCookie.extension)
- {
- _platform.XI2.OnEvent((XIEvent*)xev.GenericEventCookie.data);
- }
+ _platform.XI2.OnEvent((XIEvent*)xev.GenericEventCookie.data);
}
- else if (_eventHandlers.TryGetValue(xev.AnyEvent.window, out var handler))
- handler(xev);
- }
- finally
- {
- if (xev.type == XEventName.GenericEvent && xev.GenericEventCookie.data != null)
- XFreeEventData(_display, &xev.GenericEventCookie);
}
+ else if (_eventHandlers.TryGetValue(xev.AnyEvent.window, out var handler))
+ handler(xev);
+ }
+ finally
+ {
+ if (xev.type == XEventName.GenericEvent && xev.GenericEventCookie.data != null)
+ XFreeEventData(_display, &xev.GenericEventCookie);
}
}
+
Dispatcher.UIThread.RunJobs();
}
diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs
index 14a9bc7c1a..cbff4e38cf 100644
--- a/src/Avalonia.X11/X11Window.cs
+++ b/src/Avalonia.X11/X11Window.cs
@@ -12,6 +12,7 @@ using Avalonia.OpenGL;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Threading;
+using Avalonia.X11.Glx;
using static Avalonia.X11.XLib;
// ReSharper disable IdentifierTypo
// ReSharper disable StringLiteralTypo
@@ -45,7 +46,7 @@ namespace Avalonia.X11
}
private readonly Queue _inputQueue = new Queue();
private InputEventContainer _lastEvent;
-
+ private bool _useRenderWindow = false;
public X11Window(AvaloniaX11Platform platform, bool popup)
{
_platform = platform;
@@ -53,9 +54,8 @@ namespace Avalonia.X11
_x11 = platform.Info;
_mouse = platform.MouseDevice;
_keyboard = platform.KeyboardDevice;
- _xic = XCreateIC(_x11.Xim, XNames.XNInputStyle, XIMProperties.XIMPreeditNothing | XIMProperties.XIMStatusNothing,
- XNames.XNClientWindow, _handle, IntPtr.Zero);
-
+
+ var glfeature = AvaloniaLocator.Current.GetService();
XSetWindowAttributes attr = new XSetWindowAttributes();
var valueMask = default(SetWindowValuemask);
@@ -71,16 +71,44 @@ namespace Avalonia.X11
attr.override_redirect = true;
valueMask |= SetWindowValuemask.OverrideRedirect;
}
+
+ XVisualInfo? visualInfo = null;
+
+ // OpenGL seems to be do weird things to it's current window which breaks resize sometimes
+ _useRenderWindow = glfeature != null;
+
+ var glx = glfeature as GlxGlPlatformFeature;
+ if (glx != null)
+ visualInfo = *glx.Display.VisualInfo;
+ else if (glfeature == null)
+ visualInfo = _x11.TransparentVisualInfo;
+
+ var egl = glfeature as EglGlPlatformFeature;
+ var visual = IntPtr.Zero;
+ var depth = 24;
+ if (visualInfo != null)
+ {
+ visual = visualInfo.Value.visual;
+ depth = (int)visualInfo.Value.depth;
+ attr.colormap = XCreateColormap(_x11.Display, _x11.RootWindow, visualInfo.Value.visual, 0);
+ valueMask |= SetWindowValuemask.ColorMap;
+ }
+
_handle = XCreateWindow(_x11.Display, _x11.RootWindow, 10, 10, 300, 200, 0,
- 24,
- (int)CreateWindowArgs.InputOutput, IntPtr.Zero,
+ depth,
+ (int)CreateWindowArgs.InputOutput,
+ visual,
new UIntPtr((uint)valueMask), ref attr);
- _renderHandle = XCreateWindow(_x11.Display, _handle, 0, 0, 300, 200, 0, 24,
- (int)CreateWindowArgs.InputOutput,
- IntPtr.Zero,
- new UIntPtr((uint)(SetWindowValuemask.BorderPixel | SetWindowValuemask.BitGravity |
- SetWindowValuemask.WinGravity | SetWindowValuemask.BackingStore)), ref attr);
+
+ if (_useRenderWindow)
+ _renderHandle = XCreateWindow(_x11.Display, _handle, 0, 0, 300, 200, 0, depth,
+ (int)CreateWindowArgs.InputOutput,
+ visual,
+ new UIntPtr((uint)(SetWindowValuemask.BorderPixel | SetWindowValuemask.BitGravity |
+ SetWindowValuemask.WinGravity | SetWindowValuemask.BackingStore)), ref attr);
+ else
+ _renderHandle = _handle;
Handle = new PlatformHandle(_handle, "XID");
_realSize = new PixelSize(300, 200);
@@ -99,18 +127,26 @@ namespace Avalonia.X11
XSetWMProtocols(_x11.Display, _handle, protocols, protocols.Length);
XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_WINDOW_TYPE, _x11.Atoms.XA_ATOM,
32, PropertyMode.Replace, new[] {_x11.Atoms._NET_WM_WINDOW_TYPE_NORMAL}, 1);
-
- var feature = (EglGlPlatformFeature)AvaloniaLocator.Current.GetService();
+
+
var surfaces = new List