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/readme.md b/readme.md
index 9f16405726..3f4840fce2 100644
--- a/readme.md
+++ b/readme.md
@@ -7,7 +7,7 @@
A multi-platform .NET UI framework. It can run on Windows, Linux, Mac OS X, iOS and Android.
-
+[](https://youtu.be/wHcB3sGLVYg)
Desktop platforms:
@@ -36,7 +36,7 @@ Try out the ControlCatalog to give it a quick demo.
Avalonia is a multi-platform windowing toolkit - somewhat like WPF - that is intended to be multi-
platform. It supports XAML, lookless controls and a flexible styling system, and runs on Windows
-using Direct2D and other operating systems using Gtk & Cairo.
+using Direct2D and other operating systems using Skia and OS-specific windowing backend (GTK, Cocoa, etc).
## Current Status
diff --git a/samples/ControlCatalog/Pages/ToolTipPage.xaml b/samples/ControlCatalog/Pages/ToolTipPage.xaml
index 5cf7fee4d1..aa7d60bd11 100644
--- a/samples/ControlCatalog/Pages/ToolTipPage.xaml
+++ b/samples/ControlCatalog/Pages/ToolTipPage.xaml
@@ -10,7 +10,7 @@
HorizontalAlignment="Center">
@@ -24,7 +24,7 @@
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.Controls/StackPanel.cs b/src/Avalonia.Controls/StackPanel.cs
index 0e12fb3283..a6fe35d668 100644
--- a/src/Avalonia.Controls/StackPanel.cs
+++ b/src/Avalonia.Controls/StackPanel.cs
@@ -170,6 +170,15 @@ namespace Avalonia.Controls
}
}
+ if (Orientation == Orientation.Vertical)
+ {
+ measuredHeight -= gap;
+ }
+ else
+ {
+ measuredWidth -= gap;
+ }
+
return new Size(measuredWidth, measuredHeight);
}
diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs
index 5d1b9a1462..079e571d29 100644
--- a/src/Avalonia.Controls/TreeView.cs
+++ b/src/Avalonia.Controls/TreeView.cs
@@ -253,9 +253,7 @@ namespace Avalonia.Controls
if (AutoScrollToSelectedItem)
{
- DispatcherTimer.RunOnce(
- container.ContainerControl.BringIntoView,
- TimeSpan.Zero);
+ Dispatcher.UIThread.InvokeAsync(container.ContainerControl.BringIntoView);
}
break;
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/Avalonia.Themes.Default/RepeatButton.xaml b/src/Avalonia.Themes.Default/RepeatButton.xaml
index 557e2d3b03..1caaa266de 100644
--- a/src/Avalonia.Themes.Default/RepeatButton.xaml
+++ b/src/Avalonia.Themes.Default/RepeatButton.xaml
@@ -1,13 +1,13 @@
\ No newline at end of file
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs
index 59a895a22f..f56e7448a7 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs
@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using Avalonia.Media;
-using Avalonia.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
@@ -12,20 +11,16 @@ namespace Avalonia.Rendering.SceneGraph
///
/// Base class for draw operations that can use a brush.
///
- internal abstract class BrushDrawOperation : IDrawOperation
+ internal abstract class BrushDrawOperation : DrawOperation
{
- ///
- public abstract Rect Bounds { get; }
-
- ///
- public abstract bool HitTest(Point p);
+ public BrushDrawOperation(Rect bounds, Matrix transform, Pen pen)
+ : base(bounds, transform, pen)
+ {
+ }
///
/// Gets a collection of child scenes that are needed to draw visual brushes.
///
public abstract IDictionary ChildScenes { get; }
-
- ///
- public abstract void Render(IDrawingContextImpl context);
}
}
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/DrawOperation.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/DrawOperation.cs
new file mode 100644
index 0000000000..4c6ed189ff
--- /dev/null
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/DrawOperation.cs
@@ -0,0 +1,26 @@
+using System;
+using Avalonia.Media;
+using Avalonia.Platform;
+
+namespace Avalonia.Rendering.SceneGraph
+{
+ ///
+ /// Base class for draw operations that have bounds.
+ ///
+ internal abstract class DrawOperation : IDrawOperation
+ {
+ public DrawOperation(Rect bounds, Matrix transform, Pen pen)
+ {
+ bounds = bounds.Inflate((pen?.Thickness ?? 0) / 2).TransformToAABB(transform);
+ Bounds = new Rect(
+ new Point(Math.Floor(bounds.X), Math.Floor(bounds.Y)),
+ new Point(Math.Ceiling(bounds.Right), Math.Ceiling(bounds.Bottom)));
+ }
+
+ public Rect Bounds { get; }
+
+ public abstract bool HitTest(Point p);
+
+ public abstract void Render(IDrawingContextImpl context);
+ }
+}
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs
index b884c42d99..6310122183 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs
@@ -28,8 +28,8 @@ namespace Avalonia.Rendering.SceneGraph
Pen pen,
IGeometryImpl geometry,
IDictionary childScenes = null)
+ : base(geometry.GetRenderBounds(pen?.Thickness ?? 0), transform, null)
{
- Bounds = geometry.GetRenderBounds(pen?.Thickness ?? 0).TransformToAABB(transform);
Transform = transform;
Brush = brush?.ToImmutable();
Pen = pen?.ToImmutable();
@@ -37,9 +37,6 @@ namespace Avalonia.Rendering.SceneGraph
ChildScenes = childScenes;
}
- ///
- public override Rect Bounds { get; }
-
///
/// Gets the transform with which the node will be drawn.
///
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/IDrawOperation.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/IDrawOperation.cs
index 8c3bb72463..839fd9b0e5 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/IDrawOperation.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/IDrawOperation.cs
@@ -12,7 +12,7 @@ namespace Avalonia.Rendering.SceneGraph
public interface IDrawOperation
{
///
- /// Gets the bounds of the visible content in the node.
+ /// Gets the bounds of the visible content in the node in global coordinates.
///
Rect Bounds { get; }
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs
index 4a50f12095..8291d1c0bb 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs
@@ -9,7 +9,7 @@ namespace Avalonia.Rendering.SceneGraph
///
/// A node in the scene graph which represents an image draw.
///
- internal class ImageNode : IDrawOperation
+ internal class ImageNode : DrawOperation
{
///
/// Initializes a new instance of the class.
@@ -20,8 +20,8 @@ namespace Avalonia.Rendering.SceneGraph
/// The source rect.
/// The destination rect.
public ImageNode(Matrix transform, IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
+ : base(destRect, transform, null)
{
- Bounds = destRect.TransformToAABB(transform);
Transform = transform;
Source = source;
Opacity = opacity;
@@ -29,9 +29,6 @@ namespace Avalonia.Rendering.SceneGraph
DestRect = destRect;
}
- ///
- public Rect Bounds { get; }
-
///
/// Gets the transform with which the node will be drawn.
///
@@ -80,7 +77,7 @@ namespace Avalonia.Rendering.SceneGraph
}
///
- public void Render(IDrawingContextImpl context)
+ public override void Render(IDrawingContextImpl context)
{
// TODO: Probably need to introduce some kind of locking mechanism in the case of
// WriteableBitmap.
@@ -89,6 +86,6 @@ namespace Avalonia.Rendering.SceneGraph
}
///
- public bool HitTest(Point p) => Bounds.Contains(p);
+ public override bool HitTest(Point p) => Bounds.Contains(p);
}
}
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs
index e39335b5b6..d3df478a63 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs
@@ -28,8 +28,8 @@ namespace Avalonia.Rendering.SceneGraph
Point p1,
Point p2,
IDictionary childScenes = null)
+ : base(new Rect(p1, p2), transform, pen)
{
- Bounds = new Rect(p1, p2).TransformToAABB(transform).Inflate(pen?.Thickness ?? 0);
Transform = transform;
Pen = pen?.ToImmutable();
P1 = p1;
@@ -37,9 +37,6 @@ namespace Avalonia.Rendering.SceneGraph
ChildScenes = childScenes;
}
- ///
- public override Rect Bounds { get; }
-
///
/// Gets the transform with which the node will be drawn.
///
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/OpacityMaskNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/OpacityMaskNode.cs
index c40869724f..28b8f53e26 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/OpacityMaskNode.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/OpacityMaskNode.cs
@@ -19,6 +19,7 @@ namespace Avalonia.Rendering.SceneGraph
/// The bounds of the mask.
/// Child scenes for drawing visual brushes.
public OpacityMaskNode(IBrush mask, Rect bounds, IDictionary childScenes = null)
+ : base(Rect.Empty, Matrix.Identity, null)
{
Mask = mask?.ToImmutable();
MaskBounds = bounds;
@@ -30,12 +31,10 @@ namespace Avalonia.Rendering.SceneGraph
/// opacity mask pop.
///
public OpacityMaskNode()
+ : base(Rect.Empty, Matrix.Identity, null)
{
}
- ///
- public override Rect Bounds => Rect.Empty;
-
///
/// Gets the mask to be pushed or null if the operation represents a pop.
///
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs
index 2affc454b5..1730621c55 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs
@@ -30,8 +30,8 @@ namespace Avalonia.Rendering.SceneGraph
Rect rect,
float cornerRadius,
IDictionary childScenes = null)
+ : base(rect, transform, pen)
{
- Bounds = rect.TransformToAABB(transform).Inflate(pen?.Thickness ?? 0);
Transform = transform;
Brush = brush?.ToImmutable();
Pen = pen?.ToImmutable();
@@ -40,9 +40,6 @@ namespace Avalonia.Rendering.SceneGraph
ChildScenes = childScenes;
}
- ///
- public override Rect Bounds { get; }
-
///
/// Gets the transform with which the node will be drawn.
///
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs
index 058f3b1c22..6328d7dd14 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs
@@ -28,8 +28,8 @@ namespace Avalonia.Rendering.SceneGraph
Point origin,
IFormattedTextImpl text,
IDictionary childScenes = null)
+ : base(new Rect(origin, text.Size), transform, null)
{
- Bounds = new Rect(origin, text.Size).TransformToAABB(transform);
Transform = transform;
Foreground = foreground?.ToImmutable();
Origin = origin;
@@ -37,9 +37,6 @@ namespace Avalonia.Rendering.SceneGraph
ChildScenes = childScenes;
}
- ///
- public override Rect Bounds { get; }
-
///
/// Gets the transform with which the node will be drawn.
///
diff --git a/src/Gtk/Avalonia.Gtk3/FramebufferManager.cs b/src/Gtk/Avalonia.Gtk3/FramebufferManager.cs
index 00d1ec05f3..455b63b89e 100644
--- a/src/Gtk/Avalonia.Gtk3/FramebufferManager.cs
+++ b/src/Gtk/Avalonia.Gtk3/FramebufferManager.cs
@@ -5,6 +5,7 @@ using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Platform;
+using Avalonia.Threading;
namespace Avalonia.Gtk3
{
@@ -27,7 +28,38 @@ namespace Avalonia.Gtk3
var s = _window.ClientSize;
var width = (int) s.Width;
var height = (int) s.Height;
- return new ImageSurfaceFramebuffer(_window, width, height);
+
+ if (!Dispatcher.UIThread.CheckAccess() && Gtk3Platform.DisplayClassName.ToLower().Contains("x11"))
+ {
+ var x11 = LockX11Framebuffer(width, height);
+ if (x11 != null)
+ return x11;
+ }
+
+
+ return new ImageSurfaceFramebuffer(_window, width, height, _window.LastKnownScaleFactor);
+ }
+
+ private static int X11ErrorHandler(IntPtr d, IntPtr e)
+ {
+ return 0;
+ }
+
+ private static X11.XErrorHandler X11ErrorHandlerDelegate = X11ErrorHandler;
+
+ private static IntPtr X11Display;
+ private ILockedFramebuffer LockX11Framebuffer(int width, int height)
+ {
+ if (!_window.GdkWindowHandle.HasValue)
+ return null;
+ if (X11Display == IntPtr.Zero)
+ {
+ X11Display = X11.XOpenDisplay(IntPtr.Zero);
+ if (X11Display == IntPtr.Zero)
+ return null;
+ X11.XSetErrorHandler(X11ErrorHandlerDelegate);
+ }
+ return new X11Framebuffer(X11Display, _window.GdkWindowHandle.Value, width, height, _window.LastKnownScaleFactor);
}
}
}
diff --git a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
index b36a1cda91..10405037ab 100644
--- a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
+++ b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -22,11 +23,15 @@ namespace Avalonia.Gtk3
internal static readonly MouseDevice Mouse = new MouseDevice();
internal static readonly KeyboardDevice Keyboard = new KeyboardDevice();
internal static IntPtr App { get; set; }
+ internal static string DisplayClassName;
public static bool UseDeferredRendering = true;
public static void Initialize()
{
Resolver.Resolve();
Native.GtkInit(0, IntPtr.Zero);
+ var disp = Native.GdkGetDefaultDisplay();
+ DisplayClassName = Utf8Buffer.StringFromPtr(Native.GTypeName(Marshal.ReadIntPtr(Marshal.ReadIntPtr(disp))));
+
using (var utf = new Utf8Buffer("avalonia.app." + Guid.NewGuid()))
App = Native.GtkApplicationNew(utf, 0);
//Mark current thread as UI thread
diff --git a/src/Gtk/Avalonia.Gtk3/IDeferredRenderOperation.cs b/src/Gtk/Avalonia.Gtk3/IDeferredRenderOperation.cs
index 8c1456726c..e16463a2ef 100644
--- a/src/Gtk/Avalonia.Gtk3/IDeferredRenderOperation.cs
+++ b/src/Gtk/Avalonia.Gtk3/IDeferredRenderOperation.cs
@@ -4,6 +4,6 @@ namespace Avalonia.Gtk3
{
public interface IDeferredRenderOperation : IDisposable
{
- void RenderNow();
+ void RenderNow(IntPtr? ctx);
}
}
\ No newline at end of file
diff --git a/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs b/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs
index 34a95df47e..efda21c753 100644
--- a/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs
+++ b/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs
@@ -16,23 +16,23 @@ 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)
+ public ImageSurfaceFramebuffer(WindowBaseImpl impl, int width, int height, int factor)
{
_impl = impl;
_widget = impl.GtkWidget;
- _factor = (int)(Native.GtkWidgetGetScaleFactor?.Invoke(_widget) ?? 1u);
+ _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,15 +83,15 @@ 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;
+ _surface = surface ?? throw new ArgumentNullException();
_factor = factor;
_width = width;
_height = height;
@@ -103,9 +103,12 @@ namespace Avalonia.Gtk3
_surface = null;
}
- public void RenderNow()
+ public void RenderNow(IntPtr? ctx)
{
- DrawToWidget(_widget, _surface, _width, _height, _factor);
+ if(ctx.HasValue)
+ Draw(ctx.Value, _surface.Surface, _factor);
+ else
+ DrawToWidget(_widget, _surface.Surface, _width, _height, _factor);
}
}
@@ -116,9 +119,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 a6a08c3614..15b3a11fbb 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);
@@ -178,7 +181,7 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
public delegate void cairo_surface_destroy(IntPtr surface);
-
+
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
public delegate void cairo_set_source_surface(IntPtr cr, CairoSurface surface, double x, double y);
@@ -236,17 +239,17 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
public delegate GdkWindowState gdk_window_get_state(IntPtr window);
- [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
- public delegate void gdk_window_iconify(IntPtr window);
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+ public delegate void gtk_window_iconify(GtkWindow window);
- [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
- public delegate void gdk_window_deiconify(IntPtr window);
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+ public delegate void gtk_window_deiconify(GtkWindow window);
- [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
- public delegate void gdk_window_maximize(IntPtr window);
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+ public delegate void gtk_window_maximize(GtkWindow window);
- [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
- public delegate void gdk_window_unmaximize(IntPtr window);
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+ public delegate void gtk_window_unmaximize(GtkWindow window);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
public delegate void gtk_window_set_geometry_hints(GtkWindow window, IntPtr geometry_widget, ref GdkGeometry geometry, GdkWindowHints geom_mask);
@@ -315,6 +318,9 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
public delegate void g_object_ref(GObject instance);
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
+ public delegate IntPtr g_type_name(IntPtr instance);
+
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
public delegate ulong g_signal_connect_object(GObject instance, Utf8Buffer signal, IntPtr handler, IntPtr userData, int flags);
@@ -407,6 +413,7 @@ namespace Avalonia.Gtk3.Interop
public static D.gtk_dialog_add_button GtkDialogAddButton;
public static D.g_object_unref GObjectUnref;
public static D.g_object_ref GObjectRef;
+ public static D.g_type_name GTypeName;
public static D.g_signal_connect_object GSignalConnectObject;
public static D.g_signal_handler_disconnect GSignalHandlerDisconnect;
public static D.g_timeout_add GTimeoutAdd;
@@ -437,10 +444,10 @@ namespace Avalonia.Gtk3.Interop
public static D.gdk_window_get_origin GdkWindowGetOrigin;
public static D.gdk_window_get_pointer GdkWindowGetPointer;
public static D.gdk_window_get_state GdkWindowGetState;
- public static D.gdk_window_iconify GdkWindowIconify;
- public static D.gdk_window_deiconify GdkWindowDeiconify;
- public static D.gdk_window_maximize GdkWindowMaximize;
- public static D.gdk_window_unmaximize GdkWindowUnmaximize;
+ public static D.gtk_window_iconify GtkWindowIconify;
+ public static D.gtk_window_deiconify GtkWindowDeiconify;
+ public static D.gtk_window_maximize GtkWindowMaximize;
+ public static D.gtk_window_unmaximize GtkWindowUnmaximize;
public static D.gdk_window_begin_move_drag GdkWindowBeginMoveDrag;
public static D.gdk_window_begin_resize_drag GdkWindowBeginResizeDrag;
public static D.gdk_event_request_motions GdkEventRequestMotions;
@@ -459,6 +466,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 a3b5d57fdc..e14ed77877 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,7 +30,8 @@ namespace Avalonia.Gtk3
private GCHandle _gcHandle;
private object _lock = new object();
private IDeferredRenderOperation _nextRenderOperation;
-
+ private readonly AutoResetEvent _canSetNextOperation = new AutoResetEvent(true);
+ internal IntPtr? GdkWindowHandle;
public WindowBaseImpl(GtkWindow gtkWidget)
{
@@ -53,12 +55,8 @@ namespace Avalonia.Gtk3
ConnectEvent("leave-notify-event", OnLeaveNotifyEvent);
Connect("destroy", OnDestroy);
Native.GtkWidgetRealize(gtkWidget);
+ GdkWindowHandle = this.Handle.Handle;
_lastSize = ClientSize;
- GlibTimeout.Add(0, 16, () =>
- {
- Invalidate(default(Rect));
- return true;
- });
if (Gtk3Platform.UseDeferredRendering)
{
Native.GtkWidgetSetDoubleBuffered(gtkWidget, false);
@@ -138,7 +136,7 @@ namespace Avalonia.Gtk3
? RawMouseEventType.LeftButtonDown
: evnt->button == 3 ? RawMouseEventType.RightButtonDown : RawMouseEventType.MiddleButtonDown,
new Point(evnt->x, evnt->y), GetModifierKeys(evnt->state));
- Input?.Invoke(e);
+ OnInput(e);
return true;
}
@@ -166,7 +164,7 @@ namespace Avalonia.Gtk3
_inputRoot,
RawMouseEventType.Move,
position, GetModifierKeys(evnt->state));
- Input(e);
+ OnInput(e);
return true;
}
@@ -195,7 +193,7 @@ namespace Avalonia.Gtk3
}
var e = new RawMouseWheelEventArgs(Gtk3Platform.Mouse, evnt->time, _inputRoot,
new Point(evnt->x, evnt->y), delta, GetModifierKeys(evnt->state));
- Input(e);
+ OnInput(e);
return true;
}
@@ -210,7 +208,7 @@ namespace Avalonia.Gtk3
evnt->time,
evnt->type == GdkEventType.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
Avalonia.Gtk.Common.KeyTransform.ConvertKey((GdkKey)evnt->keyval), GetModifierKeys((GdkModifierType)evnt->state));
- Input(e);
+ OnInput(e);
return true;
}
@@ -218,7 +216,7 @@ namespace Avalonia.Gtk3
{
var evnt = (GdkEventCrossing*) pev;
var position = new Point(evnt->x, evnt->y);
- Input(new RawMouseEventArgs(Gtk3Platform.Mouse,
+ OnInput(new RawMouseEventArgs(Gtk3Platform.Mouse,
evnt->time,
_inputRoot,
RawMouseEventType.Move,
@@ -228,7 +226,7 @@ namespace Avalonia.Gtk3
private unsafe bool OnCommit(IntPtr gtkwidget, IntPtr utf8string, IntPtr userdata)
{
- Input(new RawTextInputEventArgs(Gtk3Platform.Keyboard, _lastKbdEvent, Utf8Buffer.StringFromPtr(utf8string)));
+ OnInput(new RawTextInputEventArgs(Gtk3Platform.Keyboard, _lastKbdEvent, Utf8Buffer.StringFromPtr(utf8string)));
return true;
}
@@ -260,11 +258,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()
@@ -277,10 +283,11 @@ namespace Avalonia.Gtk3
op = _nextRenderOperation;
_nextRenderOperation = null;
}
+ _canSetNextOperation.Set();
}
if (op != null)
{
- op?.RenderNow();
+ op?.RenderNow(null);
op?.Dispose();
}
}
@@ -311,7 +318,7 @@ namespace Avalonia.Gtk3
public IMouseDevice MouseDevice => Gtk3Platform.Mouse;
- public double Scaling => (double) 1 / (Native.GtkWidgetGetScaleFactor?.Invoke(GtkWidget) ?? 1);
+ public double Scaling => LastKnownScaleFactor = (int) (Native.GtkWidgetGetScaleFactor?.Invoke(GtkWidget) ?? 1);
public IPlatformHandle Handle => this;
@@ -338,6 +345,11 @@ namespace Avalonia.Gtk3
public void SetInputRoot(IInputRoot inputRoot) => _inputRoot = inputRoot;
+ void OnInput(RawInputEventArgs args)
+ {
+ Dispatcher.UIThread.InvokeAsync(() => Input?.Invoke(args), DispatcherPriority.Input);
+ }
+
public Point PointToClient(Point point)
{
int x, y;
@@ -387,6 +399,7 @@ namespace Avalonia.Gtk3
public Size ClientSize { get; private set; }
+ public int LastKnownScaleFactor { get; private set; }
public void Resize(Size value)
{
diff --git a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs
index a4bc45c0a1..c586661a7a 100644
--- a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs
+++ b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs
@@ -32,15 +32,14 @@ namespace Avalonia.Gtk3
}
set
{
- var w = Native.GtkWidgetGetWindow(GtkWidget);
if (value == WindowState.Minimized)
- Native.GdkWindowIconify(w);
+ Native.GtkWindowIconify(GtkWidget);
else if (value == WindowState.Maximized)
- Native.GdkWindowMaximize(w);
+ Native.GtkWindowMaximize(GtkWidget);
else
{
- Native.GdkWindowUnmaximize(w);
- Native.GdkWindowDeiconify(w);
+ Native.GtkWindowUnmaximize(GtkWidget);
+ Native.GtkWindowDeiconify(GtkWidget);
}
}
}
diff --git a/src/Gtk/Avalonia.Gtk3/X11.cs b/src/Gtk/Avalonia.Gtk3/X11.cs
new file mode 100644
index 0000000000..6708ece17b
--- /dev/null
+++ b/src/Gtk/Avalonia.Gtk3/X11.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Avalonia.Gtk3
+{
+ class X11
+ {
+ [DllImport("libX11.so.6")]
+ public static extern IntPtr XOpenDisplay(IntPtr name);
+
+ [DllImport("libX11.so.6")]
+ public static extern IntPtr XFreeGC(IntPtr display, IntPtr gc);
+
+ [DllImport("libX11.so.6")]
+ public static extern IntPtr XCreateGC(IntPtr display, IntPtr drawable, ulong valuemask, IntPtr values);
+
+ [DllImport("libX11.so.6")]
+ public static extern int XInitImage(ref XImage image);
+
+ [DllImport("libX11.so.6")]
+ public static extern int XDestroyImage(ref XImage image);
+
+ [DllImport("libX11.so.6")]
+ public static extern IntPtr XSetErrorHandler(XErrorHandler handler);
+
+ public delegate int XErrorHandler(IntPtr display, IntPtr error);
+
+ [DllImport("libX11.so.6")]
+ public static extern int XPutImage(IntPtr display, IntPtr drawable, IntPtr gc, ref XImage image,
+ int srcx, int srcy, int destx, int desty, uint width, uint height);
+
+
+ public unsafe struct XImage
+ {
+ public int width, height; /* size of image */
+ public int xoffset; /* number of pixels offset in X direction */
+ public int format; /* XYBitmap, XYPixmap, ZPixmap */
+ public IntPtr data; /* pointer to image data */
+ public int byte_order; /* data byte order, LSBFirst, MSBFirst */
+ public int bitmap_unit; /* quant. of scanline 8, 16, 32 */
+ public int bitmap_bit_order; /* LSBFirst, MSBFirst */
+ public int bitmap_pad; /* 8, 16, 32 either XY or ZPixmap */
+ public int depth; /* depth of image */
+ public int bytes_per_line; /* accelerator to next scanline */
+ public int bits_per_pixel; /* bits per pixel (ZPixmap) */
+ public ulong red_mask; /* bits in z arrangement */
+ public ulong green_mask;
+ public ulong blue_mask;
+ private fixed byte funcs[128];
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/src/Gtk/Avalonia.Gtk3/X11Framebuffer.cs b/src/Gtk/Avalonia.Gtk3/X11Framebuffer.cs
new file mode 100644
index 0000000000..2bf08bddf1
--- /dev/null
+++ b/src/Gtk/Avalonia.Gtk3/X11Framebuffer.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Runtime.InteropServices;
+using Avalonia.Platform;
+
+namespace Avalonia.Gtk3
+{
+ class X11Framebuffer : ILockedFramebuffer
+ {
+ private readonly IntPtr _display;
+ private readonly IntPtr _xid;
+ private IUnmanagedBlob _blob;
+
+ public X11Framebuffer(IntPtr display, IntPtr xid, int width, int height, int factor)
+ {
+ _display = display;
+ _xid = xid;
+ Width = width*factor;
+ Height = height*factor;
+ RowBytes = Width * 4;
+ Dpi = new Vector(96, 96) * factor;
+ Format = PixelFormat.Bgra8888;
+ _blob = AvaloniaLocator.Current.GetService().AllocBlob(RowBytes * Height);
+ Address = _blob.Address;
+ }
+
+ public void Dispose()
+ {
+ var image = new X11.XImage();
+ int bitsPerPixel = 32;
+ image.width = Width;
+ image.height = Height;
+ image.format = 2; //ZPixmap;
+ image.data = Address;
+ image.byte_order = 0;// LSBFirst;
+ image.bitmap_unit = bitsPerPixel;
+ image.bitmap_bit_order = 0;// LSBFirst;
+ image.bitmap_pad = bitsPerPixel;
+ image.depth = 24;
+ image.bytes_per_line = RowBytes - Width * 4;
+ image.bits_per_pixel = bitsPerPixel;
+ X11.XInitImage(ref image);
+ var gc = X11.XCreateGC(_display, _xid, 0, IntPtr.Zero);
+ X11.XPutImage(_display, _xid, gc, ref image, 0, 0, 0, 0, (uint) Width, (uint) Height);
+ X11.XFreeGC(_display, gc);
+ _blob.Dispose();
+ }
+
+ public IntPtr Address { get; }
+ public int Width { get; }
+ public int Height { get; }
+ public int RowBytes { get; }
+ public Vector Dpi { get; }
+ public PixelFormat Format { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Shared/PlatformSupport/StandardRuntimePlatform.cs b/src/Shared/PlatformSupport/StandardRuntimePlatform.cs
index 092910a08f..b777736f06 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,145 @@ 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;
+ Size = 0;
+ }
+ }
+
+ 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..00ab770e01 100644
--- a/src/Skia/Avalonia.Skia/BitmapImpl.cs
+++ b/src/Skia/Avalonia.Skia/BitmapImpl.cs
@@ -20,16 +20,35 @@ 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;
PixelWidth = width;
_dpi = dpi;
var colorType = fmt?.ToSkColorType() ?? SKImageInfo.PlatformColorType;
- var runtime = AvaloniaLocator.Current?.GetService()?.GetRuntimeInfo();
+ var runtimePlatform = AvaloniaLocator.Current?.GetService();
+ var runtime = runtimePlatform?.GetRuntimeInfo();
if (runtime?.IsDesktop == true && runtime?.OperatingSystem == OperatingSystemType.Linux)
colorType = SKColorType.Bgra8888;
- Bitmap = new SKBitmap(width, height, colorType, SKAlphaType.Premul);
+
+ if (runtimePlatform != null)
+ {
+ 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);
+
+ }
+ else
+ Bitmap = new SKBitmap(width, height, colorType, SKAlphaType.Premul);
Bitmap.Erase(SKColor.Empty);
}
diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
index 4feb910deb..e549f06bd5 100644
--- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
+++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
@@ -45,7 +45,7 @@ namespace Avalonia.Skia
var s = sourceRect.ToSKRect();
var d = destRect.ToSKRect();
using (var paint = new SKPaint()
- { Color = new SKColor(255, 255, 255, (byte)(255 * opacity)) })
+ { Color = new SKColor(255, 255, 255, (byte)(255 * opacity * _currentOpacity)) })
{
Canvas.DrawBitmap(impl.Bitmap, s, d, paint);
}
@@ -112,6 +112,7 @@ namespace Avalonia.Skia
public readonly SKPaint Paint;
private IDisposable _disposable1;
+ private IDisposable _disposable2;
public IDisposable ApplyTo(SKPaint paint)
{
@@ -127,6 +128,8 @@ namespace Avalonia.Skia
{
if (_disposable1 == null)
_disposable1 = disposable;
+ else if (_disposable2 == null)
+ _disposable2 = disposable;
else
throw new InvalidOperationException();
}
@@ -135,12 +138,14 @@ namespace Avalonia.Skia
{
Paint = paint;
_disposable1 = null;
+ _disposable2 = null;
}
public void Dispose()
{
Paint?.Dispose();
_disposable1?.Dispose();
+ _disposable2?.Dispose();
}
}
@@ -221,8 +226,8 @@ namespace Avalonia.Skia
_visualBrushRenderer.RenderVisualBrush(ctx, visualBrush);
}
- rv.AddDisposable(tileBrushImage);
tileBrushImage = intermediate;
+ rv.AddDisposable(tileBrushImage);
}
}
else
diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
index fd8364c03b..90409bb208 100644
--- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
+++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
@@ -146,6 +146,8 @@ namespace Avalonia.Direct2D1
}
if (s is IExternalDirect2DRenderTargetSurface external)
return new ExternalRenderTarget(external, s_dwfactory);
+ if (s is IFramebufferPlatformSurface fb)
+ return new FramebufferShimRenderTarget(fb, s_imagingFactory, s_d2D1Factory, s_dwfactory);
}
throw new NotSupportedException("Don't know how to create a Direct2D1 renderer from any of provided surfaces");
}
diff --git a/src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs b/src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs
new file mode 100644
index 0000000000..d465af5322
--- /dev/null
+++ b/src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Avalonia.Controls.Platform.Surfaces;
+using Avalonia.Direct2D1.Media;
+using Avalonia.Direct2D1.Media.Imaging;
+using Avalonia.Platform;
+using Avalonia.Rendering;
+using Avalonia.Win32.Interop;
+using SharpDX.Direct2D1;
+using SharpDX.WIC;
+using PixelFormat = Avalonia.Platform.PixelFormat;
+
+namespace Avalonia.Direct2D1
+{
+ class FramebufferShimRenderTarget : IRenderTarget
+ {
+ private readonly IFramebufferPlatformSurface _surface;
+ private readonly ImagingFactory _imagingFactory;
+ private readonly Factory _d2DFactory;
+ private readonly SharpDX.DirectWrite.Factory _dwriteFactory;
+
+ public FramebufferShimRenderTarget(IFramebufferPlatformSurface surface,
+ ImagingFactory imagingFactory, Factory d2dFactory, SharpDX.DirectWrite.Factory dwriteFactory)
+ {
+ _surface = surface;
+ _imagingFactory = imagingFactory;
+ _d2DFactory = d2dFactory;
+ _dwriteFactory = dwriteFactory;
+ }
+
+ public void Dispose()
+ {
+
+ }
+
+ public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
+ {
+ var locked = _surface.Lock();
+ if (locked.Format == PixelFormat.Rgb565)
+ {
+ locked.Dispose();
+ throw new ArgumentException("Unsupported pixel format: " + locked.Format);
+ }
+
+ return new FramebufferShim(locked, _imagingFactory, _d2DFactory, _dwriteFactory)
+ .CreateDrawingContext(visualBrushRenderer);
+ }
+
+ class FramebufferShim : RenderTargetBitmapImpl
+ {
+ private readonly ILockedFramebuffer _target;
+
+ public FramebufferShim(ILockedFramebuffer target,
+ ImagingFactory imagingFactory, Factory d2dFactory, SharpDX.DirectWrite.Factory dwriteFactory
+ ) : base(imagingFactory, d2dFactory, dwriteFactory,
+ target.Width, target.Height, target.Dpi.X, target.Dpi.Y, target.Format)
+ {
+ _target = target;
+ }
+
+ public override IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
+ {
+ return base.CreateDrawingContext(visualBrushRenderer, () =>
+ {
+ using (var l = WicImpl.Lock(BitmapLockFlags.Read))
+ {
+ for (var y = 0; y < _target.Height; y++)
+ {
+ UnmanagedMethods.CopyMemory(
+ _target.Address + _target.RowBytes * y,
+ l.Data.DataPointer + l.Stride * y,
+ (uint) Math.Min(l.Stride, _target.RowBytes));
+ }
+ }
+ Dispose();
+ _target.Dispose();
+
+ });
+ }
+ }
+
+ }
+}
diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs
index 33736b02cb..0d6ed9f39f 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs
@@ -22,8 +22,9 @@ namespace Avalonia.Direct2D1.Media
int width,
int height,
double dpiX,
- double dpiY)
- : base(imagingFactory, width, height)
+ double dpiY,
+ Platform.PixelFormat? pixelFormat = null)
+ : base(imagingFactory, width, height, pixelFormat)
{
var props = new RenderTargetProperties
{
@@ -45,9 +46,13 @@ namespace Avalonia.Direct2D1.Media
base.Dispose();
}
- public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
+ public virtual IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
+ => CreateDrawingContext(visualBrushRenderer, null);
+
+ public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer, Action finishedCallback)
{
- return new DrawingContextImpl(visualBrushRenderer, _target, _dwriteFactory);
+ return new DrawingContextImpl(visualBrushRenderer, _target, _dwriteFactory,
+ finishedCallback: finishedCallback);
}
}
}
diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
index e817dd4812..bcce2496cd 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
@@ -74,6 +74,7 @@ namespace Avalonia.Direct2D1.Media
public WicBitmapImpl(ImagingFactory factory, Platform.PixelFormat format, IntPtr data, int width, int height, int stride)
{
WicImpl = new Bitmap(factory, width, height, format.ToWic(), BitmapCreateCacheOption.CacheOnDemand);
+ _factory = factory;
PixelFormat = format;
using (var l = WicImpl.Lock(BitmapLockFlags.Write))
{
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
index 05339c43b0..e9cb2bf450 100644
--- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
+++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
@@ -187,7 +187,7 @@ namespace Avalonia.Base.UnitTests
source.OnNext(45);
- Assert.Equal(null, target.Foo);
+ Assert.Null(target.Foo);
}
[Fact]
diff --git a/tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs b/tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs
index 01b550fb3b..9b4be59647 100644
--- a/tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs
@@ -66,7 +66,7 @@ namespace Avalonia.Controls.UnitTests.Generators
target.Dematerialize(1, 1);
Assert.Equal(containers[0].ContainerControl, target.ContainerFromIndex(0));
- Assert.Equal(null, target.ContainerFromIndex(1));
+ Assert.Null(target.ContainerFromIndex(1));
Assert.Equal(containers[2].ContainerControl, target.ContainerFromIndex(2));
}
diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs
index e32c703409..a524ca3e89 100644
--- a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs
+++ b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs
@@ -107,7 +107,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.Content = child;
target.Content = null;
- Assert.Equal(null, child.GetLogicalParent());
+ Assert.Null(child.GetLogicalParent());
Assert.Empty(target.GetLogicalChildren());
}
@@ -120,7 +120,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.Content = child;
target.Content = null;
- Assert.Equal(null, child.GetVisualParent());
+ Assert.Null(child.GetVisualParent());
Assert.Empty(target.GetVisualChildren());
}
diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs
index 589b1d67d2..032928d673 100644
--- a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs
+++ b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs
@@ -191,7 +191,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.Content = "bar";
target.UpdateChild();
- Assert.Equal(null, foo.Parent);
+ Assert.Null(foo.Parent);
logicalChildren = target.GetLogicalChildren();
diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
index 7ee9fbbf52..a60074fa43 100644
--- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
@@ -171,7 +171,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.SelectedItem = new Item();
- Assert.Equal(null, target.SelectedItem);
+ Assert.Null(target.SelectedItem);
Assert.Equal(-1, target.SelectedIndex);
}
@@ -278,7 +278,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.Items = null;
- Assert.Equal(null, target.SelectedItem);
+ Assert.Null(target.SelectedItem);
Assert.Equal(-1, target.SelectedIndex);
}
@@ -305,7 +305,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
items.RemoveAt(1);
- Assert.Equal(null, target.SelectedItem);
+ Assert.Null(target.SelectedItem);
Assert.Equal(-1, target.SelectedIndex);
}
@@ -334,7 +334,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
items.Clear();
- Assert.Equal(null, target.SelectedItem);
+ Assert.Null(target.SelectedItem);
Assert.Equal(-1, target.SelectedIndex);
}
diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs
index d8600f472d..642f594e4d 100644
--- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs
+++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs
@@ -76,7 +76,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.SelectedItems = new AvaloniaList