diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 39333f37ba..981b17b0de 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -32,7 +32,7 @@ jobs:
- job: macOS
pool:
- vmImage: 'xcode9-macos10.13'
+ vmImage: 'macOS-10.14'
steps:
- task: DotNetCoreInstaller@0
inputs:
@@ -49,7 +49,7 @@ jobs:
inputs:
actions: 'build'
scheme: ''
- sdk: 'macosx10.13'
+ sdk: 'macosx10.14'
configuration: 'Release'
xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace'
xcodeVersion: 'default' # Options: 8, 9, default, specifyPath
@@ -134,3 +134,4 @@ jobs:
pathToPublish: '$(Build.SourcesDirectory)/artifacts/zip'
artifactName: 'Samples'
condition: succeeded()
+
diff --git a/build/SharedVersion.props b/build/SharedVersion.props
index 7ea1dd0c65..16a4b828f4 100644
--- a/build/SharedVersion.props
+++ b/build/SharedVersion.props
@@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
Avalonia
- 0.8.0
+ 0.8.1
Copyright 2018 © The AvaloniaUI Project
https://github.com/AvaloniaUI/Avalonia/blob/master/licence.md
https://github.com/AvaloniaUI/Avalonia/
diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs
index bb31034299..84092d52eb 100644
--- a/nukebuild/Build.cs
+++ b/nukebuild/Build.cs
@@ -122,6 +122,14 @@ partial class Build : NukeBuild
foreach(var fw in frameworks)
{
+ if (fw.StartsWith("net4")
+ && RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
+ && Environment.GetEnvironmentVariable("FORCE_LINUX_TESTS") != "1")
+ {
+ Information($"Skipping {fw} tests on Linux - https://github.com/mono/mono/issues/13969");
+ continue;
+ }
+
Information("Running for " + fw);
DotNetTest(c =>
{
diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
index 589f41c06b..a25cbfc696 100644
--- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
+++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
@@ -10,6 +10,7 @@
+
diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs
index d13a5b5ef3..c58d071ac8 100644
--- a/samples/ControlCatalog.NetCore/Program.cs
+++ b/samples/ControlCatalog.NetCore/Program.cs
@@ -3,7 +3,6 @@ using System.Diagnostics;
using System.Linq;
using System.Threading;
using Avalonia;
-using Avalonia.Skia;
namespace ControlCatalog.NetCore
{
@@ -45,6 +44,10 @@ namespace ControlCatalog.NetCore
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure()
.UsePlatformDetect()
+ .With(new Win32PlatformOptions
+ {
+ AllowEglInitialization = true
+ })
.UseSkia()
.UseReactiveUI()
.UseDataGrid();
diff --git a/src/Avalonia.Native/Avalonia.Native.csproj b/src/Avalonia.Native/Avalonia.Native.csproj
index c8ee73ad5d..9d6a82a2fb 100644
--- a/src/Avalonia.Native/Avalonia.Native.csproj
+++ b/src/Avalonia.Native/Avalonia.Native.csproj
@@ -7,8 +7,6 @@
/usr/bin/castxml
/usr/local/bin/castxml
true
-
- $(MSBuildThisFileDirectory)/Generated
diff --git a/src/Avalonia.OpenGL/AngleOptions.cs b/src/Avalonia.OpenGL/AngleOptions.cs
new file mode 100644
index 0000000000..4b9c04f4e6
--- /dev/null
+++ b/src/Avalonia.OpenGL/AngleOptions.cs
@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+
+namespace Avalonia.OpenGL
+{
+ public class AngleOptions
+ {
+ public enum PlatformApi
+ {
+ DirectX9,
+ DirectX11
+ }
+
+ public List AllowedPlatformApis = new List
+ {
+ PlatformApi.DirectX9
+ };
+ }
+}
diff --git a/src/Avalonia.OpenGL/EglDisplay.cs b/src/Avalonia.OpenGL/EglDisplay.cs
index b14932acfe..b2b5a1a646 100644
--- a/src/Avalonia.OpenGL/EglDisplay.cs
+++ b/src/Avalonia.OpenGL/EglDisplay.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Runtime.InteropServices;
using Avalonia.Platform.Interop;
using static Avalonia.OpenGL.EglConsts;
@@ -13,21 +14,42 @@ namespace Avalonia.OpenGL
private readonly int[] _contextAttributes;
public IntPtr Handle => _display;
+ private AngleOptions.PlatformApi? _angleApi;
public EglDisplay(EglInterface egl)
{
_egl = egl;
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && _egl.GetPlatformDisplayEXT != null)
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- foreach (var dapi in new[] {EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE})
+ if (_egl.GetPlatformDisplayEXT == null)
+ throw new OpenGlException("eglGetPlatformDisplayEXT is not supported by libegl.dll");
+
+ var allowedApis = AvaloniaLocator.Current.GetService()?.AllowedPlatformApis
+ ?? new List {AngleOptions.PlatformApi.DirectX9};
+
+ foreach (var platformApi in allowedApis)
{
+ int dapi;
+ if (platformApi == AngleOptions.PlatformApi.DirectX9)
+ dapi = EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE;
+ else if (platformApi == AngleOptions.PlatformApi.DirectX11)
+ dapi = EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE;
+ else
+ continue;
+
_display = _egl.GetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, IntPtr.Zero, new[]
{
EGL_PLATFORM_ANGLE_TYPE_ANGLE, dapi, EGL_NONE
});
- if(_display != IntPtr.Zero)
+ if (_display != IntPtr.Zero)
+ {
+ _angleApi = platformApi;
break;
+ }
}
+
+ if (_display == IntPtr.Zero)
+ throw new OpenGlException("Unable to create ANGLE display");
}
if (_display == IntPtr.Zero)
@@ -64,29 +86,35 @@ namespace Avalonia.OpenGL
if (!_egl.BindApi(cfg.Api))
continue;
- var attribs = new[]
+ foreach(var stencilSize in new[]{8, 1, 0})
+ foreach (var depthSize in new []{8, 1, 0})
{
- EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
- EGL_RENDERABLE_TYPE, cfg.RenderableTypeBit,
- EGL_RED_SIZE, 8,
- EGL_GREEN_SIZE, 8,
- EGL_BLUE_SIZE, 8,
- EGL_ALPHA_SIZE, 8,
- EGL_STENCIL_SIZE, 8,
- EGL_DEPTH_SIZE, 8,
- EGL_NONE
- };
- if (!_egl.ChooseConfig(_display, attribs, out _config, 1, out int numConfigs))
- continue;
- if (numConfigs == 0)
- continue;
- _contextAttributes = cfg.Attributes;
- Type = cfg.Type;
+ var attribs = new[]
+ {
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+
+ EGL_RENDERABLE_TYPE, cfg.RenderableTypeBit,
+
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_STENCIL_SIZE, stencilSize,
+ EGL_DEPTH_SIZE, depthSize,
+ EGL_NONE
+ };
+ if (!_egl.ChooseConfig(_display, attribs, out _config, 1, out int numConfigs))
+ continue;
+ if (numConfigs == 0)
+ continue;
+ _contextAttributes = cfg.Attributes;
+ Type = cfg.Type;
+ }
}
if (_contextAttributes == null)
throw new OpenGlException("No suitable EGL config was found");
-
+
GlInterface = GlInterface.FromNativeUtf8GetProcAddress(b => _egl.GetProcAddress(b));
}
@@ -97,6 +125,7 @@ namespace Avalonia.OpenGL
public GlDisplayType Type { get; }
public GlInterface GlInterface { get; }
+ public EglInterface EglInterface => _egl;
public IGlContext CreateContext(IGlContext share)
{
var shareCtx = (EglContext)share;
diff --git a/src/Avalonia.OpenGL/EglGlPlatformSurface.cs b/src/Avalonia.OpenGL/EglGlPlatformSurface.cs
index f5dd413b0f..d2e4543af3 100644
--- a/src/Avalonia.OpenGL/EglGlPlatformSurface.cs
+++ b/src/Avalonia.OpenGL/EglGlPlatformSurface.cs
@@ -26,31 +26,44 @@ namespace Avalonia.OpenGL
public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
{
var glSurface = _display.CreateWindowSurface(_info.Handle);
- return new RenderTarget(_context, glSurface, _info);
+ return new RenderTarget(_display, _context, glSurface, _info);
}
- class RenderTarget : IGlPlatformSurfaceRenderTarget
+ class RenderTarget : IGlPlatformSurfaceRenderTargetWithCorruptionInfo
{
+ private readonly EglDisplay _display;
private readonly EglContext _context;
private readonly EglSurface _glSurface;
private readonly IEglWindowGlPlatformSurfaceInfo _info;
+ private PixelSize _initialSize;
- public RenderTarget(EglContext context, EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info)
+ public RenderTarget(EglDisplay display, EglContext context,
+ EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info)
{
+ _display = display;
_context = context;
_glSurface = glSurface;
_info = info;
+ _initialSize = info.Size;
}
public void Dispose() => _glSurface.Dispose();
+ public bool IsCorrupted => _initialSize != _info.Size;
+
public IGlPlatformSurfaceRenderingSession BeginDraw()
{
var l = _context.Lock();
try
{
+ if (IsCorrupted)
+ throw new RenderTargetCorruptedException();
_context.MakeCurrent(_glSurface);
- return new Session(_context, _glSurface, _info, l);
+ _display.EglInterface.WaitClient();
+ _display.EglInterface.WaitGL();
+ _display.EglInterface.WaitNative();
+
+ return new Session(_display, _context, _glSurface, _info, l);
}
catch
{
@@ -61,15 +74,19 @@ namespace Avalonia.OpenGL
class Session : IGlPlatformSurfaceRenderingSession
{
- private readonly IGlContext _context;
+ private readonly EglContext _context;
private readonly EglSurface _glSurface;
private readonly IEglWindowGlPlatformSurfaceInfo _info;
+ private readonly EglDisplay _display;
private IDisposable _lock;
+
- public Session(IGlContext context, EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info,
+ public Session(EglDisplay display, EglContext context,
+ EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info,
IDisposable @lock)
{
_context = context;
+ _display = display;
_glSurface = glSurface;
_info = info;
_lock = @lock;
@@ -78,7 +95,11 @@ namespace Avalonia.OpenGL
public void Dispose()
{
_context.Display.GlInterface.Flush();
+ _display.EglInterface.WaitGL();
_glSurface.SwapBuffers();
+ _display.EglInterface.WaitClient();
+ _display.EglInterface.WaitGL();
+ _display.EglInterface.WaitNative();
_context.Display.ClearContext();
_lock.Dispose();
}
diff --git a/src/Avalonia.OpenGL/EglInterface.cs b/src/Avalonia.OpenGL/EglInterface.cs
index 00fcd97af0..fd1374f3fe 100644
--- a/src/Avalonia.OpenGL/EglInterface.cs
+++ b/src/Avalonia.OpenGL/EglInterface.cs
@@ -1,4 +1,5 @@
using System;
+using System.Runtime.InteropServices;
using Avalonia.Platform;
using Avalonia.Platform.Interop;
@@ -91,6 +92,31 @@ namespace Avalonia.OpenGL
[GlEntryPoint("eglGetConfigAttrib")]
public EglGetConfigAttrib GetConfigAttrib { get; }
+ public delegate bool EglWaitGL();
+ [GlEntryPoint("eglWaitGL")]
+ public EglWaitGL WaitGL { get; }
+
+ public delegate bool EglWaitClient();
+ [GlEntryPoint("eglWaitClient")]
+ public EglWaitGL WaitClient { get; }
+
+ public delegate bool EglWaitNative();
+ [GlEntryPoint("eglWaitNative")]
+ public EglWaitGL WaitNative { get; }
+
+ public delegate IntPtr EglQueryString(IntPtr display, int i);
+
+ [GlEntryPoint("eglQueryString")]
+ public EglQueryString QueryStringNative { get; }
+
+ public string QueryString(IntPtr display, int i)
+ {
+ var rv = QueryStringNative(display, i);
+ if (rv == IntPtr.Zero)
+ return null;
+ return Marshal.PtrToStringAnsi(rv);
+ }
+
// ReSharper restore UnassignedGetOnlyAutoProperty
}
}
diff --git a/src/Avalonia.OpenGL/GlInterface.cs b/src/Avalonia.OpenGL/GlInterface.cs
index 718afc4a94..f556949cfa 100644
--- a/src/Avalonia.OpenGL/GlInterface.cs
+++ b/src/Avalonia.OpenGL/GlInterface.cs
@@ -9,12 +9,14 @@ namespace Avalonia.OpenGL
public class GlInterface : GlInterfaceBase
{
public string Version { get; }
+ public string Vendor { get; }
+ public string Renderer { get; }
public GlInterface(Func getProcAddress) : base(getProcAddress)
{
- var versionPtr = GetString(GlConsts.GL_VERSION);
- if (versionPtr != IntPtr.Zero)
- Version = Marshal.PtrToStringAnsi(versionPtr);
+ Version = GetString(GlConsts.GL_VERSION);
+ Renderer = GetString(GlConsts.GL_RENDERER);
+ Vendor = GetString(GlConsts.GL_VENDOR);
}
public GlInterface(Func n) : this(ConvertNative(n))
@@ -54,7 +56,15 @@ namespace Avalonia.OpenGL
public delegate IntPtr GlGetString(int v);
[GlEntryPoint("glGetString")]
- public GlGetString GetString { get; }
+ public GlGetString GetStringNative { get; }
+
+ public string GetString(int v)
+ {
+ var ptr = GetStringNative(v);
+ if (ptr != IntPtr.Zero)
+ return Marshal.PtrToStringAnsi(ptr);
+ return null;
+ }
public delegate void GlGetIntegerv(int name, out int rv);
[GlEntryPoint("glGetIntegerv")]
diff --git a/src/Avalonia.OpenGL/IGlPlatformSurfaceRenderTarget.cs b/src/Avalonia.OpenGL/IGlPlatformSurfaceRenderTarget.cs
index 53da93315c..d198d46e5c 100644
--- a/src/Avalonia.OpenGL/IGlPlatformSurfaceRenderTarget.cs
+++ b/src/Avalonia.OpenGL/IGlPlatformSurfaceRenderTarget.cs
@@ -6,4 +6,9 @@ namespace Avalonia.OpenGL
{
IGlPlatformSurfaceRenderingSession BeginDraw();
}
-}
\ No newline at end of file
+
+ public interface IGlPlatformSurfaceRenderTargetWithCorruptionInfo : IGlPlatformSurfaceRenderTarget
+ {
+ bool IsCorrupted { get; }
+ }
+}
diff --git a/src/Avalonia.Visuals/Platform/IRenderTarget.cs b/src/Avalonia.Visuals/Platform/IRenderTarget.cs
index 522de64ec7..516bea782e 100644
--- a/src/Avalonia.Visuals/Platform/IRenderTarget.cs
+++ b/src/Avalonia.Visuals/Platform/IRenderTarget.cs
@@ -23,4 +23,9 @@ namespace Avalonia.Platform
///
IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer);
}
+
+ public interface IRenderTargetWithCorruptionInfo : IRenderTarget
+ {
+ bool IsCorrupted { get; }
+ }
}
diff --git a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
index 5293e1b978..6f2ffe916d 100644
--- a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
+++ b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
@@ -248,6 +248,11 @@ namespace Avalonia.Rendering
{
if (context != null)
return context;
+ if ((RenderTarget as IRenderTargetWithCorruptionInfo)?.IsCorrupted == true)
+ {
+ RenderTarget.Dispose();
+ RenderTarget = null;
+ }
if (RenderTarget == null)
RenderTarget = ((IRenderRoot)_root).CreateRenderTarget();
return context = RenderTarget.CreateDrawingContext(this);
diff --git a/src/Avalonia.Visuals/Rendering/ManagedDeferredRendererLock.cs b/src/Avalonia.Visuals/Rendering/ManagedDeferredRendererLock.cs
index 75d8f036d6..2d4a39e026 100644
--- a/src/Avalonia.Visuals/Rendering/ManagedDeferredRendererLock.cs
+++ b/src/Avalonia.Visuals/Rendering/ManagedDeferredRendererLock.cs
@@ -7,11 +7,25 @@ namespace Avalonia.Rendering
public class ManagedDeferredRendererLock : IDeferredRendererLock
{
private readonly object _lock = new object();
+
+ ///
+ /// Tries to lock the target surface or window
+ ///
+ /// IDisposable if succeeded to obtain the lock
public IDisposable TryLock()
{
if (Monitor.TryEnter(_lock))
return Disposable.Create(() => Monitor.Exit(_lock));
return null;
}
+
+ ///
+ /// Enters a waiting lock, only use from platform code, not from the renderer
+ ///
+ public IDisposable Lock()
+ {
+ Monitor.Enter(_lock);
+ return Disposable.Create(() => Monitor.Exit(_lock));
+ }
}
}
diff --git a/src/Avalonia.Visuals/Rendering/UiThreadRenderTimer.cs b/src/Avalonia.Visuals/Rendering/UiThreadRenderTimer.cs
new file mode 100644
index 0000000000..dd6cf7ad15
--- /dev/null
+++ b/src/Avalonia.Visuals/Rendering/UiThreadRenderTimer.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Diagnostics;
+using System.Reactive.Disposables;
+using Avalonia.Threading;
+
+namespace Avalonia.Rendering
+{
+ ///
+ /// Render timer that ticks on UI thread. Useful for debugging or bootstrapping on new platforms
+ ///
+
+ public class UiThreadRenderTimer : DefaultRenderTimer
+ {
+ public UiThreadRenderTimer(int framesPerSecond) : base(framesPerSecond)
+ {
+ }
+
+ protected override IDisposable StartCore(Action tick)
+ {
+ bool cancelled = false;
+ var st = Stopwatch.StartNew();
+ DispatcherTimer.Run(() =>
+ {
+ if (cancelled)
+ return false;
+ tick(st.Elapsed);
+ return !cancelled;
+ }, TimeSpan.FromSeconds(1.0 / FramesPerSecond), DispatcherPriority.Render);
+ return Disposable.Create(() => cancelled = true);
+ }
+ }
+}
diff --git a/src/Avalonia.X11/Glx/GlxDisplay.cs b/src/Avalonia.X11/Glx/GlxDisplay.cs
index 5602b33280..04f2a7137c 100644
--- a/src/Avalonia.X11/Glx/GlxDisplay.cs
+++ b/src/Avalonia.X11/Glx/GlxDisplay.cs
@@ -90,6 +90,19 @@ namespace Avalonia.X11.Glx
GlInterface = new GlInterface(GlxInterface.GlxGetProcAddress);
if (GlInterface.Version == null)
throw new OpenGlException("GL version string is null, aborting");
+ if (GlInterface.Renderer == null)
+ throw new OpenGlException("GL renderer string is null, aborting");
+
+ if (Environment.GetEnvironmentVariable("AVALONIA_GLX_IGNORE_RENDERER_BLACKLIST") != "1")
+ {
+ var blacklist = AvaloniaLocator.Current.GetService()
+ ?.GlxRendererBlacklist;
+ if (blacklist != null)
+ foreach(var item in blacklist)
+ if (GlInterface.Renderer.Contains(item))
+ throw new OpenGlException($"Renderer '{GlInterface.Renderer}' is blacklisted by '{item}'");
+ }
+
}
public void ClearContext() => Glx.MakeContextCurrent(_x11.Display,
diff --git a/src/Avalonia.X11/X11KeyTransform.cs b/src/Avalonia.X11/X11KeyTransform.cs
index 26495111d1..c68cb04733 100644
--- a/src/Avalonia.X11/X11KeyTransform.cs
+++ b/src/Avalonia.X11/X11KeyTransform.cs
@@ -221,12 +221,8 @@ namespace Avalonia.X11
//{ X11Key.?, Key.DeadCharProcessed }
};
- public static Key ConvertKey(IntPtr key)
- {
- var ikey = key.ToInt32();
- Key result;
- return KeyDic.TryGetValue((X11Key)ikey, out result) ? result : Key.None;
- }
-}
+ public static Key ConvertKey(X11Key key)
+ => KeyDic.TryGetValue(key, out var result) ? result : Key.None;
+ }
}
diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs
index ce03113169..47d5276fbc 100644
--- a/src/Avalonia.X11/X11Platform.cs
+++ b/src/Avalonia.X11/X11Platform.cs
@@ -95,6 +95,14 @@ namespace Avalonia
{
public bool UseEGL { get; set; }
public bool UseGpu { get; set; } = true;
+
+ public List GlxRendererBlacklist { get; set; } = new List
+ {
+ // llvmpipe is a software GL rasterizer. If it's returned by glGetString,
+ // that usually means that something in the system is horribly misconfigured
+ // and sometimes attempts to use GLX might cause a segfault
+ "llvmpipe"
+ };
public string WmClass { get; set; } = Assembly.GetEntryAssembly()?.GetName()?.Name ?? "AvaloniaApplication";
}
public static class AvaloniaX11PlatformExtensions
diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs
index c5e77fe352..878ed12b66 100644
--- a/src/Avalonia.X11/X11Window.cs
+++ b/src/Avalonia.X11/X11Window.cs
@@ -419,10 +419,21 @@ namespace Avalonia.X11
return;
var buffer = stackalloc byte[40];
- var latinKeysym = XKeycodeToKeysym(_x11.Display, ev.KeyEvent.keycode, 0);
+ var index = ev.KeyEvent.state.HasFlag(XModifierMask.ShiftMask);
+
+ // We need the latin key, since it's mainly used for hotkeys, we use a different API for text anyway
+ var key = (X11Key)XKeycodeToKeysym(_x11.Display, ev.KeyEvent.keycode, index ? 1 : 0).ToInt32();
+
+ // Manually switch the Shift index for the keypad,
+ // there should be a proper way to do this
+ if (ev.KeyEvent.state.HasFlag(XModifierMask.Mod2Mask)
+ && key > X11Key.Num_Lock && key <= X11Key.KP_9)
+ key = (X11Key)XKeycodeToKeysym(_x11.Display, ev.KeyEvent.keycode, index ? 0 : 1).ToInt32();
+
+
ScheduleInput(new RawKeyEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(),
ev.type == XEventName.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
- X11KeyTransform.ConvertKey(latinKeysym), TranslateModifiers(ev.KeyEvent.state)), ref ev);
+ X11KeyTransform.ConvertKey(key), TranslateModifiers(ev.KeyEvent.state)), ref ev);
if (ev.type == XEventName.KeyPress)
{
diff --git a/src/Skia/Avalonia.Skia/GlRenderTarget.cs b/src/Skia/Avalonia.Skia/GlRenderTarget.cs
index cd8c334b53..e9e1727dfe 100644
--- a/src/Skia/Avalonia.Skia/GlRenderTarget.cs
+++ b/src/Skia/Avalonia.Skia/GlRenderTarget.cs
@@ -21,6 +21,8 @@ namespace Avalonia.Skia
public void Dispose() => _surface.Dispose();
+ public bool IsCorrupted => (_surface as IGlPlatformSurfaceRenderTargetWithCorruptionInfo)?.IsCorrupted == true;
+
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
{
var session = _surface.BeginDraw();
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index 081a713e95..11d6c467de 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.cs
@@ -31,6 +31,7 @@ namespace Avalonia.Win32
private string _className;
private IntPtr _hwnd;
private IInputRoot _owner;
+ private ManagedDeferredRendererLock _rendererLock = new ManagedDeferredRendererLock();
private bool _trackingMouse;
private bool _decorated = true;
private bool _resizable = true;
@@ -148,7 +149,9 @@ namespace Avalonia.Win32
if (customRendererFactory != null)
return customRendererFactory.Create(root, loop);
- return Win32Platform.UseDeferredRendering ? (IRenderer)new DeferredRenderer(root, loop) : new ImmediateRenderer(root);
+ return Win32Platform.UseDeferredRendering ?
+ (IRenderer)new DeferredRenderer(root, loop, rendererLock: _rendererLock) :
+ new ImmediateRenderer(root);
}
public void Resize(Size value)
@@ -627,18 +630,26 @@ namespace Avalonia.Win32
break;
case UnmanagedMethods.WindowsMessage.WM_PAINT:
- UnmanagedMethods.PAINTSTRUCT ps;
- if (UnmanagedMethods.BeginPaint(_hwnd, out ps) != IntPtr.Zero)
+ using (_rendererLock.Lock())
{
- var f = Scaling;
- var r = ps.rcPaint;
- Paint?.Invoke(new Rect(r.left / f, r.top / f, (r.right - r.left) / f, (r.bottom - r.top) / f));
- UnmanagedMethods.EndPaint(_hwnd, ref ps);
+ UnmanagedMethods.PAINTSTRUCT ps;
+ if (UnmanagedMethods.BeginPaint(_hwnd, out ps) != IntPtr.Zero)
+ {
+ var f = Scaling;
+ var r = ps.rcPaint;
+ Paint?.Invoke(new Rect(r.left / f, r.top / f, (r.right - r.left) / f,
+ (r.bottom - r.top) / f));
+ UnmanagedMethods.EndPaint(_hwnd, ref ps);
+ }
}
return IntPtr.Zero;
case UnmanagedMethods.WindowsMessage.WM_SIZE:
+ using (_rendererLock.Lock())
+ {
+ // Do nothing here, just block until the pending frame render is completed on the render thread
+ }
var size = (UnmanagedMethods.SizeCommand)wParam;
if (Resized != null &&
@@ -704,7 +715,8 @@ namespace Avalonia.Win32
}
}
- return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam);
+ using (_rendererLock.Lock())
+ return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam);
}
static InputModifiers GetMouseModifiers(IntPtr wParam)