diff --git a/appveyor.yml b/appveyor.yml
index cef8c4a2cc..aa8c19ace4 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -16,9 +16,7 @@ environment:
init:
- ps: if (Test-Path env:nuget_address) {[System.IO.File]::AppendAllText("C:\Windows\System32\drivers\etc\hosts", "`n$($env:nuget_address)`tapi.nuget.org")}
install:
- - if not exist gtk-sharp-2.12.26.msi appveyor DownloadFile http://download.xamarin.com/GTKforWindows/Windows/gtk-sharp-2.12.26.msi
- if not exist dotnet-2.0.0.exe appveyor DownloadFile https://download.microsoft.com/download/0/F/D/0FD852A4-7EA1-4E2A-983A-0484AC19B92C/dotnet-sdk-2.0.0-win-x64.exe -FileName "dotnet-2.0.0.exe"
- - ps: Start-Process -FilePath "msiexec" -ArgumentList "/i gtk-sharp-2.12.26.msi /quiet /qn /norestart" -Wait
- ps: Start-Process -FilePath "dotnet-2.0.0.exe" -ArgumentList "/quiet" -Wait
- cmd: set PATH=%programfiles(x86)%\GtkSharp\2.12\bin\;%PATH%
before_build:
@@ -36,5 +34,4 @@ artifacts:
- path: artifacts\zip\*.zip
- path: artifacts\inspectcode.xml
cache:
- - gtk-sharp-2.12.26.msi
- dotnet-2.0.0.exe
diff --git a/build/NetCore.props b/build/NetCore.props
index cebb02c7f5..b9cde28015 100644
--- a/build/NetCore.props
+++ b/build/NetCore.props
@@ -1,6 +1,4 @@
-
-
diff --git a/docs/guidelines/build.md b/docs/guidelines/build.md
index 828a4ddab3..559790b197 100644
--- a/docs/guidelines/build.md
+++ b/docs/guidelines/build.md
@@ -4,12 +4,6 @@
Avalonia requires at least Visual Studio 2017 and .NET Core SDK 2.0 to build on Windows.
-### Install GTK Sharp
-
-For the moment under windows, you must have [gtk-sharp](http://www.mono-project.com/download/#download-win)
-installed. Note that after installing the package your machine may require a restart before GTK# is
-added to your path. We hope to remove or make this dependency optional at some point in the future.
-
### Clone the Avalonia repository
```
diff --git a/src/Avalonia.Base/Platform/IRuntimePlatform.cs b/src/Avalonia.Base/Platform/IRuntimePlatform.cs
index e1a09f094d..66253dc5b2 100644
--- a/src/Avalonia.Base/Platform/IRuntimePlatform.cs
+++ b/src/Avalonia.Base/Platform/IRuntimePlatform.cs
@@ -14,6 +14,15 @@ namespace Avalonia.Platform
IDisposable StartSystemTimer(TimeSpan interval, Action tick);
string GetStackTrace();
RuntimePlatformInfo GetRuntimeInfo();
+ IUnmanagedBlob AllocBlob(int size);
+ }
+
+ public interface IUnmanagedBlob : IDisposable
+ {
+ IntPtr Address { get; }
+ int Size { get; }
+ bool IsDisposed { get; }
+
}
public struct RuntimePlatformInfo
diff --git a/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj b/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj
index eade213c4c..53b2c997d0 100644
--- a/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj
+++ b/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj
@@ -2,6 +2,7 @@
netcoreapp2.0
false
+ $(DefineConstants);DOTNETCORE
bin\$(Configuration)\Avalonia.DotNetCoreRuntime.XML
@@ -21,5 +22,5 @@
-
+
\ No newline at end of file
diff --git a/src/Avalonia.DotNetCoreRuntime/NetCoreRuntimePlatform.cs b/src/Avalonia.DotNetCoreRuntime/NetCoreRuntimePlatform.cs
deleted file mode 100644
index 6aab8e0243..0000000000
--- a/src/Avalonia.DotNetCoreRuntime/NetCoreRuntimePlatform.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-using System.Threading.Tasks;
-using Microsoft.DotNet.PlatformAbstractions;
-using Microsoft.Extensions.DependencyModel;
-
-namespace Avalonia.Shared.PlatformSupport
-{
- internal partial class StandardRuntimePlatform
- {
- private static readonly Lazy Assemblies = new Lazy(LoadAssemblies);
- public Assembly[] GetLoadedAssemblies() => Assemblies.Value;
-
- static Assembly[] LoadAssemblies()
- {
- var assemblies = new List();
- // Mostly copy-pasted from (MIT):
- // https://github.com/StefH/System.AppDomain.Core/blob/0b35e676c2721aa367b96e62eb52c97ee0b43a70/src/System.AppDomain.NetCoreApp/AppDomain.cs
-
- foreach (var assemblyName in
- DependencyContext.Default.GetRuntimeAssemblyNames(RuntimeEnvironment.GetRuntimeIdentifier()))
- {
- try
- {
- var assembly = Assembly.Load(assemblyName);
- // just load all types and skip this assembly if one or more types cannot be resolved
- assembly.DefinedTypes.ToArray();
- assemblies.Add(assembly);
- }
- catch (Exception ex)
- {
- Debug.Write(ex.Message);
- }
- }
- return assemblies.ToArray();
- }
- }
-}
diff --git a/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs b/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs
index 182da7df58..4ebb080dd0 100644
--- a/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs
+++ b/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs
@@ -16,7 +16,7 @@ namespace Avalonia.Gtk3
{
private readonly WindowBaseImpl _impl;
private readonly GtkWidget _widget;
- private CairoSurface _surface;
+ private ManagedCairoSurface _surface;
private int _factor;
private object _lock = new object();
public ImageSurfaceFramebuffer(WindowBaseImpl impl, int width, int height, int factor)
@@ -26,13 +26,13 @@ namespace Avalonia.Gtk3
_factor = factor;
width *= _factor;
height *= _factor;
- _surface = Native.CairoImageSurfaceCreate(1, width, height);
+ _surface = new ManagedCairoSurface(width, height);
Width = width;
Height = height;
- Address = Native.CairoImageSurfaceGetData(_surface);
- RowBytes = Native.CairoImageSurfaceGetStride(_surface);
- Native.CairoSurfaceFlush(_surface);
+ Address = _surface.Buffer;
+ RowBytes = _surface.Stride;
+ Native.CairoSurfaceFlush(_surface.Surface);
}
static void Draw(IntPtr context, CairoSurface surface, double factor)
@@ -83,12 +83,12 @@ namespace Avalonia.Gtk3
class RenderOp : IDeferredRenderOperation
{
private readonly GtkWidget _widget;
- private CairoSurface _surface;
+ private ManagedCairoSurface _surface;
private readonly double _factor;
private readonly int _width;
private readonly int _height;
- public RenderOp(GtkWidget widget, CairoSurface _surface, double factor, int width, int height)
+ public RenderOp(GtkWidget widget, ManagedCairoSurface _surface, double factor, int width, int height)
{
_widget = widget;
this._surface = _surface;
@@ -105,7 +105,7 @@ namespace Avalonia.Gtk3
public void RenderNow()
{
- DrawToWidget(_widget, _surface, _width, _height, _factor);
+ DrawToWidget(_widget, _surface.Surface, _width, _height, _factor);
}
}
@@ -116,9 +116,9 @@ namespace Avalonia.Gtk3
if (Dispatcher.UIThread.CheckAccess())
{
if (_impl.CurrentCairoContext != IntPtr.Zero)
- Draw(_impl.CurrentCairoContext, _surface, _factor);
+ Draw(_impl.CurrentCairoContext, _surface.Surface, _factor);
else
- DrawToWidget(_widget, _surface, Width, Height, _factor);
+ DrawToWidget(_widget, _surface.Surface, Width, Height, _factor);
_surface.Dispose();
}
else
diff --git a/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs b/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs
index be886ea1c7..5253be5afb 100644
--- a/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs
+++ b/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs
@@ -53,7 +53,7 @@ namespace Avalonia.Gtk3.Interop
if (interval == 0)
throw new ArgumentException("Don't know how to create a timer with zero or negative interval");
var timer = new Timer ();
- GlibTimeout.Add(GlibPriority.FromDispatcherPriority(DispatcherPriority.Background), interval,
+ GlibTimeout.Add(GlibPriority.FromDispatcherPriority(DispatcherPriority.Normal), interval,
() =>
{
if (timer.Stopped)
diff --git a/src/Gtk/Avalonia.Gtk3/Interop/ManagedCairoSurface.cs b/src/Gtk/Avalonia.Gtk3/Interop/ManagedCairoSurface.cs
new file mode 100644
index 0000000000..2cde99afa5
--- /dev/null
+++ b/src/Gtk/Avalonia.Gtk3/Interop/ManagedCairoSurface.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Runtime.InteropServices;
+using Avalonia.Platform;
+
+namespace Avalonia.Gtk3.Interop
+{
+ class ManagedCairoSurface : IDisposable
+ {
+ public IntPtr Buffer { get; private set; }
+ public CairoSurface Surface { get; private set; }
+ public int Stride { get; private set; }
+ private int _size;
+ private IRuntimePlatform _plat;
+ private IUnmanagedBlob _blob;
+
+ public ManagedCairoSurface(int width, int height)
+ {
+ _plat = AvaloniaLocator.Current.GetService();
+ Stride = width * 4;
+ _size = height * Stride;
+ _blob = _plat.AllocBlob(_size * 2);
+ Buffer = _blob.Address;
+ Surface = Native.CairoImageSurfaceCreateForData(Buffer, 1, width, height, Stride);
+ }
+
+ public void Dispose()
+ {
+
+ if (Buffer != IntPtr.Zero)
+ {
+ Surface.Dispose();
+ _blob.Dispose();
+ Buffer = IntPtr.Zero;
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs
index c96fb23366..433094e0ae 100644
--- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs
+++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs
@@ -160,6 +160,9 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
public delegate CairoSurface cairo_image_surface_create(int format, int width, int height);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
+ public delegate CairoSurface cairo_image_surface_create_for_data(IntPtr data, int format, int width, int height, int stride);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
public delegate IntPtr cairo_image_surface_get_data(CairoSurface surface);
@@ -459,6 +462,7 @@ namespace Avalonia.Gtk3.Interop
public static D.gdk_cairo_create GdkCairoCreate;
public static D.cairo_image_surface_create CairoImageSurfaceCreate;
+ public static D.cairo_image_surface_create_for_data CairoImageSurfaceCreateForData;
public static D.cairo_image_surface_get_data CairoImageSurfaceGetData;
public static D.cairo_image_surface_get_stride CairoImageSurfaceGetStride;
public static D.cairo_surface_mark_dirty CairoSurfaceMarkDirty;
diff --git a/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
index bfec3db756..8771106895 100644
--- a/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
+++ b/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
@@ -4,6 +4,7 @@ using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
+using System.Threading;
using Avalonia.Controls;
using Avalonia.Gtk3.Interop;
using Avalonia.Input;
@@ -29,6 +30,7 @@ namespace Avalonia.Gtk3
private GCHandle _gcHandle;
private object _lock = new object();
private IDeferredRenderOperation _nextRenderOperation;
+ private readonly AutoResetEvent _canSetNextOperation = new AutoResetEvent(true);
public WindowBaseImpl(GtkWindow gtkWidget)
{
@@ -255,11 +257,19 @@ namespace Avalonia.Gtk3
public void SetNextRenderOperation(IDeferredRenderOperation op)
{
- lock (_lock)
+ while (true)
{
- _nextRenderOperation?.Dispose();
- _nextRenderOperation = op;
+ lock (_lock)
+ {
+ if (_nextRenderOperation == null)
+ {
+ _nextRenderOperation = op;
+ return;
+ }
+ }
+ _canSetNextOperation.WaitOne();
}
+
}
private void OnRenderTick()
@@ -272,6 +282,7 @@ namespace Avalonia.Gtk3
op = _nextRenderOperation;
_nextRenderOperation = null;
}
+ _canSetNextOperation.Set();
}
if (op != null)
{
diff --git a/src/Shared/PlatformSupport/StandardRuntimePlatform.cs b/src/Shared/PlatformSupport/StandardRuntimePlatform.cs
index 092910a08f..fafa17a810 100644
--- a/src/Shared/PlatformSupport/StandardRuntimePlatform.cs
+++ b/src/Shared/PlatformSupport/StandardRuntimePlatform.cs
@@ -2,8 +2,11 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
+using System.Collections.Generic;
using System.Reflection;
using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Threading;
using Avalonia.Platform;
@@ -11,20 +14,144 @@ namespace Avalonia.Shared.PlatformSupport
{
internal partial class StandardRuntimePlatform : IRuntimePlatform
{
-
-#if NETCOREAPP2_0
- public void PostThreadPoolItem(Action cb) => ThreadPool.QueueUserWorkItem(_ => cb(), null);
-#else
- public Assembly[] GetLoadedAssemblies() => AppDomain.CurrentDomain.GetAssemblies();
public void PostThreadPoolItem(Action cb) => ThreadPool.UnsafeQueueUserWorkItem(_ => cb(), null);
-#endif
+ public Assembly[] GetLoadedAssemblies() => AppDomain.CurrentDomain.GetAssemblies();
public IDisposable StartSystemTimer(TimeSpan interval, Action tick)
{
return new Timer(_ => tick(), null, interval, interval);
}
+ public string GetStackTrace() => Environment.StackTrace;
+
+ public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(this, size);
+
+ class UnmanagedBlob : IUnmanagedBlob
+ {
+ private readonly StandardRuntimePlatform _plat;
+#if DEBUG
+ private static readonly List Backtraces = new List();
+ private static Thread GCThread;
+ private readonly string _backtrace;
- public string GetStackTrace() => Environment.StackTrace;
+ class GCThreadDetector
+ {
+ ~GCThreadDetector()
+ {
+ GCThread = Thread.CurrentThread;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static void Spawn() => new GCThreadDetector();
+
+ static UnmanagedBlob()
+ {
+ Spawn();
+ GC.WaitForPendingFinalizers();
+ }
+
+#endif
+
+ public UnmanagedBlob(StandardRuntimePlatform plat, int size)
+ {
+ _plat = plat;
+ Address = plat.Alloc(size);
+ GC.AddMemoryPressure(size);
+ Size = size;
+#if DEBUG
+ _backtrace = Environment.StackTrace;
+ Backtraces.Add(_backtrace);
+#endif
+ }
+
+ void DoDispose()
+ {
+ if (!IsDisposed)
+ {
+#if DEBUG
+ Backtraces.Remove(_backtrace);
+#endif
+ _plat.Free(Address, Size);
+ GC.RemoveMemoryPressure(Size);
+ IsDisposed = true;
+ Address = IntPtr.Zero;
+ }
+ }
+
+ public void Dispose()
+ {
+#if DEBUG
+ if (Thread.CurrentThread.ManagedThreadId == GCThread?.ManagedThreadId)
+ {
+ Console.Error.WriteLine("Native blob disposal from finalizer thread\nBacktrace: "
+ + Environment.StackTrace
+ + "\n\nBlob created by " + _backtrace);
+ }
+#endif
+ DoDispose();
+ GC.SuppressFinalize(this);
+ }
+
+ ~UnmanagedBlob()
+ {
+#if DEBUG
+ Console.Error.WriteLine("Undisposed native blob created by " + _backtrace);
+#endif
+ DoDispose();
+ }
+
+ public IntPtr Address { get; private set; }
+ public int Size { get; private set; }
+ public bool IsDisposed { get; private set; }
+ }
+
+
+
+#if FULLDOTNET || DOTNETCORE
+ [DllImport("libc", SetLastError = true)]
+ private static extern IntPtr mmap(IntPtr addr, IntPtr length, int prot, int flags, int fd, IntPtr offset);
+ [DllImport("libc", SetLastError = true)]
+ private static extern int munmap(IntPtr addr, IntPtr length);
+ [DllImport("libc", SetLastError = true)]
+ private static extern long sysconf(int name);
+
+ private bool? _useMmap;
+ private bool UseMmap
+ => _useMmap ?? ((_useMmap = GetRuntimeInfo().OperatingSystem == OperatingSystemType.Linux)).Value;
+
+ IntPtr Alloc(int size)
+ {
+ if (UseMmap)
+ {
+ var rv = mmap(IntPtr.Zero, new IntPtr(size), 3, 0x22, -1, IntPtr.Zero);
+ if (rv.ToInt64() == -1 || (ulong) rv.ToInt64() == 0xffffffff)
+ {
+ var errno = Marshal.GetLastWin32Error();
+ throw new Exception("Unable to allocate memory: " + errno);
+ }
+ return rv;
+ }
+ else
+ return Marshal.AllocHGlobal(size);
+ }
+
+ void Free(IntPtr ptr, int len)
+ {
+ if (UseMmap)
+ {
+ if (munmap(ptr, new IntPtr(len)) == -1)
+ {
+ var errno = Marshal.GetLastWin32Error();
+ throw new Exception("Unable to free memory: " + errno);
+ }
+ }
+ else
+ Marshal.FreeHGlobal(ptr);
+ }
+#else
+ IntPtr Alloc(int size) => Marshal.AllocHGlobal(size);
+ void Free(IntPtr ptr, int len) => Marshal.FreeHGlobal(ptr);
+#endif
}
}
\ No newline at end of file
diff --git a/src/Skia/Avalonia.Skia/BitmapImpl.cs b/src/Skia/Avalonia.Skia/BitmapImpl.cs
index ce3efded11..d13190deff 100644
--- a/src/Skia/Avalonia.Skia/BitmapImpl.cs
+++ b/src/Skia/Avalonia.Skia/BitmapImpl.cs
@@ -20,6 +20,13 @@ namespace Avalonia.Skia
_dpi = new Vector(96, 96);
}
+ static void ReleaseProc(IntPtr address, object ctx)
+ {
+ ((IUnmanagedBlob) ctx).Dispose();
+ }
+
+ private static readonly SKBitmapReleaseDelegate ReleaseDelegate = ReleaseProc;
+
public BitmapImpl(int width, int height, Vector dpi, PixelFormat? fmt = null)
{
PixelHeight = height;
@@ -29,6 +36,12 @@ namespace Avalonia.Skia
var runtime = AvaloniaLocator.Current?.GetService()?.GetRuntimeInfo();
if (runtime?.IsDesktop == true && runtime?.OperatingSystem == OperatingSystemType.Linux)
colorType = SKColorType.Bgra8888;
+
+ Bitmap = new SKBitmap();
+ var nfo = new SKImageInfo(width, height, colorType, SKAlphaType.Premul);
+ var plat = AvaloniaLocator.Current.GetService();
+ var blob = plat.AllocBlob(nfo.BytesSize);
+ Bitmap.InstallPixels(nfo, blob.Address, nfo.RowBytes, null, ReleaseDelegate, blob);
Bitmap = new SKBitmap(width, height, colorType, SKAlphaType.Premul);
Bitmap.Erase(SKColor.Empty);
}