Browse Source

Merge remote-tracking branch 'origin/master' into pr/1161-fixed-226

pull/1161/head
Steven Kirk 9 years ago
parent
commit
b133cb8588
  1. 3
      appveyor.yml
  2. 2
      build/NetCore.props
  3. 6
      docs/guidelines/build.md
  4. 4
      readme.md
  5. 4
      samples/ControlCatalog/Pages/ToolTipPage.xaml
  6. 9
      src/Avalonia.Base/Platform/IRuntimePlatform.cs
  7. 9
      src/Avalonia.Controls/StackPanel.cs
  8. 4
      src/Avalonia.Controls/TreeView.cs
  9. 3
      src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj
  10. 42
      src/Avalonia.DotNetCoreRuntime/NetCoreRuntimePlatform.cs
  11. 14
      src/Avalonia.Themes.Default/RepeatButton.xaml
  12. 15
      src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs
  13. 26
      src/Avalonia.Visuals/Rendering/SceneGraph/DrawOperation.cs
  14. 5
      src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs
  15. 2
      src/Avalonia.Visuals/Rendering/SceneGraph/IDrawOperation.cs
  16. 11
      src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs
  17. 5
      src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs
  18. 5
      src/Avalonia.Visuals/Rendering/SceneGraph/OpacityMaskNode.cs
  19. 5
      src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs
  20. 5
      src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs
  21. 34
      src/Gtk/Avalonia.Gtk3/FramebufferManager.cs
  22. 5
      src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
  23. 2
      src/Gtk/Avalonia.Gtk3/IDeferredRenderOperation.cs
  24. 31
      src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs
  25. 2
      src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs
  26. 38
      src/Gtk/Avalonia.Gtk3/Interop/ManagedCairoSurface.cs
  27. 34
      src/Gtk/Avalonia.Gtk3/Interop/Native.cs
  28. 47
      src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
  29. 9
      src/Gtk/Avalonia.Gtk3/WindowImpl.cs
  30. 54
      src/Gtk/Avalonia.Gtk3/X11.cs
  31. 55
      src/Gtk/Avalonia.Gtk3/X11Framebuffer.cs
  32. 142
      src/Shared/PlatformSupport/StandardRuntimePlatform.cs
  33. 23
      src/Skia/Avalonia.Skia/BitmapImpl.cs
  34. 9
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  35. 2
      src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
  36. 84
      src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs
  37. 13
      src/Windows/Avalonia.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs
  38. 1
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
  39. 2
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
  40. 2
      tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs
  41. 4
      tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs
  42. 2
      tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs
  43. 8
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
  44. 2
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs
  45. 4
      tests/Avalonia.Controls.UnitTests/StackPanelTests.cs
  46. 2
      tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_RelativeSource.cs
  47. 4
      tests/Avalonia.Markup.Xaml.UnitTests/Templates/MemberSelectorTests.cs
  48. 11
      tests/Avalonia.RenderTests/Media/BitmapTests.cs
  49. 55
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs
  50. BIN
      tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Bgra8888.expected.png
  51. BIN
      tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Rgb565.expected.png
  52. BIN
      tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Rgba8888.expected.png

3
appveyor.yml

@ -16,9 +16,7 @@ environment:
init: 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")} - 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: 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" - 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 - ps: Start-Process -FilePath "dotnet-2.0.0.exe" -ArgumentList "/quiet" -Wait
- cmd: set PATH=%programfiles(x86)%\GtkSharp\2.12\bin\;%PATH% - cmd: set PATH=%programfiles(x86)%\GtkSharp\2.12\bin\;%PATH%
before_build: before_build:
@ -36,5 +34,4 @@ artifacts:
- path: artifacts\zip\*.zip - path: artifacts\zip\*.zip
- path: artifacts\inspectcode.xml - path: artifacts\inspectcode.xml
cache: cache:
- gtk-sharp-2.12.26.msi
- dotnet-2.0.0.exe - dotnet-2.0.0.exe

2
build/NetCore.props

@ -1,6 +1,4 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Threading.ThreadPool" Version="4.3.0" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="1.1.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

6
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. 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 ### Clone the Avalonia repository
``` ```

4
readme.md

@ -7,7 +7,7 @@
A multi-platform .NET UI framework. It can run on Windows, Linux, Mac OS X, iOS and Android. A multi-platform .NET UI framework. It can run on Windows, Linux, Mac OS X, iOS and Android.
![](docs/images/screen.png) [![](docs/images/screen.png)](https://youtu.be/wHcB3sGLVYg)
Desktop platforms: 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- 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 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 ## Current Status

4
samples/ControlCatalog/Pages/ToolTipPage.xaml

@ -10,7 +10,7 @@
HorizontalAlignment="Center"> HorizontalAlignment="Center">
<Border Grid.Column="0" <Border Grid.Column="0"
Grid.Row="1" Grid.Row="1"
Background="{StyleResource ThemeAccentBrush}" Background="{DynamicResource ThemeAccentBrush}"
Margin="5" Margin="5"
Padding="50" Padding="50"
ToolTip.Tip="This is a ToolTip"> ToolTip.Tip="This is a ToolTip">
@ -24,7 +24,7 @@
<Border Name="Border" <Border Name="Border"
Grid.Column="1" Grid.Column="1"
Grid.Row="1" Grid.Row="1"
Background="{StyleResource ThemeAccentBrush}" Background="{DynamicResource ThemeAccentBrush}"
Margin="5" Margin="5"
Padding="50" Padding="50"
ToolTip.Placement="Bottom"> ToolTip.Placement="Bottom">

9
src/Avalonia.Base/Platform/IRuntimePlatform.cs

@ -14,6 +14,15 @@ namespace Avalonia.Platform
IDisposable StartSystemTimer(TimeSpan interval, Action tick); IDisposable StartSystemTimer(TimeSpan interval, Action tick);
string GetStackTrace(); string GetStackTrace();
RuntimePlatformInfo GetRuntimeInfo(); RuntimePlatformInfo GetRuntimeInfo();
IUnmanagedBlob AllocBlob(int size);
}
public interface IUnmanagedBlob : IDisposable
{
IntPtr Address { get; }
int Size { get; }
bool IsDisposed { get; }
} }
public struct RuntimePlatformInfo public struct RuntimePlatformInfo

9
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); return new Size(measuredWidth, measuredHeight);
} }

4
src/Avalonia.Controls/TreeView.cs

@ -253,9 +253,7 @@ namespace Avalonia.Controls
if (AutoScrollToSelectedItem) if (AutoScrollToSelectedItem)
{ {
DispatcherTimer.RunOnce( Dispatcher.UIThread.InvokeAsync(container.ContainerControl.BringIntoView);
container.ContainerControl.BringIntoView,
TimeSpan.Zero);
} }
break; break;

3
src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj

@ -2,6 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<DefineConstants>$(DefineConstants);DOTNETCORE</DefineConstants>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<DocumentationFile>bin\$(Configuration)\Avalonia.DotNetCoreRuntime.XML</DocumentationFile> <DocumentationFile>bin\$(Configuration)\Avalonia.DotNetCoreRuntime.XML</DocumentationFile>
@ -21,5 +22,5 @@
<ProjectReference Include="..\Windows\Avalonia.Win32.NetStandard\Avalonia.Win32.NetStandard.csproj" /> <ProjectReference Include="..\Windows\Avalonia.Win32.NetStandard\Avalonia.Win32.NetStandard.csproj" />
</ItemGroup> </ItemGroup>
<Import Project="..\..\build\NetCore.props" /> <Import Project="..\..\build\NetCore.props" />
<Import Project="..\Shared\PlatformSupport\PlatformSupport.projitems" Label="Shared" /> <Import Project="..\Shared\PlatformSupport\PlatformSupport.projitems" />
</Project> </Project>

42
src/Avalonia.DotNetCoreRuntime/NetCoreRuntimePlatform.cs

@ -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<Assembly[]> Assemblies = new Lazy<Assembly[]>(LoadAssemblies);
public Assembly[] GetLoadedAssemblies() => Assemblies.Value;
static Assembly[] LoadAssemblies()
{
var assemblies = new List<Assembly>();
// 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();
}
}
}

14
src/Avalonia.Themes.Default/RepeatButton.xaml

@ -1,13 +1,13 @@
<Styles xmlns="https://github.com/avaloniaui"> <Styles xmlns="https://github.com/avaloniaui">
<Style Selector="RepeatButton"> <Style Selector="RepeatButton">
<Setter Property="Background" <Setter Property="Background"
Value="{StyleResource ThemeControlMidBrush}" /> Value="{DynamicResource ThemeControlMidBrush}" />
<Setter Property="BorderBrush" <Setter Property="BorderBrush"
Value="{StyleResource ThemeBorderLightBrush}" /> Value="{DynamicResource ThemeBorderLightBrush}" />
<Setter Property="BorderThickness" <Setter Property="BorderThickness"
Value="{StyleResource ThemeBorderThickness}" /> Value="{DynamicResource ThemeBorderThickness}" />
<Setter Property="Foreground" <Setter Property="Foreground"
Value="{StyleResource ThemeForegroundBrush}" /> Value="{DynamicResource ThemeForegroundBrush}" />
<Setter Property="HorizontalContentAlignment" <Setter Property="HorizontalContentAlignment"
Value="Center" /> Value="Center" />
<Setter Property="VerticalContentAlignment" <Setter Property="VerticalContentAlignment"
@ -31,14 +31,14 @@
</Style> </Style>
<Style Selector="RepeatButton:pointerover /template/ ContentPresenter"> <Style Selector="RepeatButton:pointerover /template/ ContentPresenter">
<Setter Property="BorderBrush" <Setter Property="BorderBrush"
Value="{StyleResource ThemeBorderMidBrush}" /> Value="{DynamicResource ThemeBorderMidBrush}" />
</Style> </Style>
<Style Selector="RepeatButton:pressed /template/ ContentPresenter"> <Style Selector="RepeatButton:pressed /template/ ContentPresenter">
<Setter Property="Background" <Setter Property="Background"
Value="{StyleResource ThemeControlDarkBrush}" /> Value="{DynamicResource ThemeControlDarkBrush}" />
</Style> </Style>
<Style Selector="RepeatButton:disabled"> <Style Selector="RepeatButton:disabled">
<Setter Property="Opacity" <Setter Property="Opacity"
Value="{StyleResource ThemeDisabledOpacity}" /> Value="{DynamicResource ThemeDisabledOpacity}" />
</Style> </Style>
</Styles> </Styles>

15
src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs

@ -4,7 +4,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.VisualTree; using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph namespace Avalonia.Rendering.SceneGraph
@ -12,20 +11,16 @@ namespace Avalonia.Rendering.SceneGraph
/// <summary> /// <summary>
/// Base class for draw operations that can use a brush. /// Base class for draw operations that can use a brush.
/// </summary> /// </summary>
internal abstract class BrushDrawOperation : IDrawOperation internal abstract class BrushDrawOperation : DrawOperation
{ {
/// <inheritdoc/> public BrushDrawOperation(Rect bounds, Matrix transform, Pen pen)
public abstract Rect Bounds { get; } : base(bounds, transform, pen)
{
/// <inheritdoc/> }
public abstract bool HitTest(Point p);
/// <summary> /// <summary>
/// Gets a collection of child scenes that are needed to draw visual brushes. /// Gets a collection of child scenes that are needed to draw visual brushes.
/// </summary> /// </summary>
public abstract IDictionary<IVisual, Scene> ChildScenes { get; } public abstract IDictionary<IVisual, Scene> ChildScenes { get; }
/// <inheritdoc/>
public abstract void Render(IDrawingContextImpl context);
} }
} }

26
src/Avalonia.Visuals/Rendering/SceneGraph/DrawOperation.cs

@ -0,0 +1,26 @@
using System;
using Avalonia.Media;
using Avalonia.Platform;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// Base class for draw operations that have bounds.
/// </summary>
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);
}
}

5
src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs

@ -28,8 +28,8 @@ namespace Avalonia.Rendering.SceneGraph
Pen pen, Pen pen,
IGeometryImpl geometry, IGeometryImpl geometry,
IDictionary<IVisual, Scene> childScenes = null) IDictionary<IVisual, Scene> childScenes = null)
: base(geometry.GetRenderBounds(pen?.Thickness ?? 0), transform, null)
{ {
Bounds = geometry.GetRenderBounds(pen?.Thickness ?? 0).TransformToAABB(transform);
Transform = transform; Transform = transform;
Brush = brush?.ToImmutable(); Brush = brush?.ToImmutable();
Pen = pen?.ToImmutable(); Pen = pen?.ToImmutable();
@ -37,9 +37,6 @@ namespace Avalonia.Rendering.SceneGraph
ChildScenes = childScenes; ChildScenes = childScenes;
} }
/// <inheritdoc/>
public override Rect Bounds { get; }
/// <summary> /// <summary>
/// Gets the transform with which the node will be drawn. /// Gets the transform with which the node will be drawn.
/// </summary> /// </summary>

2
src/Avalonia.Visuals/Rendering/SceneGraph/IDrawOperation.cs

@ -12,7 +12,7 @@ namespace Avalonia.Rendering.SceneGraph
public interface IDrawOperation public interface IDrawOperation
{ {
/// <summary> /// <summary>
/// Gets the bounds of the visible content in the node. /// Gets the bounds of the visible content in the node in global coordinates.
/// </summary> /// </summary>
Rect Bounds { get; } Rect Bounds { get; }

11
src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs

@ -9,7 +9,7 @@ namespace Avalonia.Rendering.SceneGraph
/// <summary> /// <summary>
/// A node in the scene graph which represents an image draw. /// A node in the scene graph which represents an image draw.
/// </summary> /// </summary>
internal class ImageNode : IDrawOperation internal class ImageNode : DrawOperation
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageNode"/> class. /// Initializes a new instance of the <see cref="ImageNode"/> class.
@ -20,8 +20,8 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="sourceRect">The source rect.</param> /// <param name="sourceRect">The source rect.</param>
/// <param name="destRect">The destination rect.</param> /// <param name="destRect">The destination rect.</param>
public ImageNode(Matrix transform, IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect) public ImageNode(Matrix transform, IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
: base(destRect, transform, null)
{ {
Bounds = destRect.TransformToAABB(transform);
Transform = transform; Transform = transform;
Source = source; Source = source;
Opacity = opacity; Opacity = opacity;
@ -29,9 +29,6 @@ namespace Avalonia.Rendering.SceneGraph
DestRect = destRect; DestRect = destRect;
} }
/// <inheritdoc/>
public Rect Bounds { get; }
/// <summary> /// <summary>
/// Gets the transform with which the node will be drawn. /// Gets the transform with which the node will be drawn.
/// </summary> /// </summary>
@ -80,7 +77,7 @@ namespace Avalonia.Rendering.SceneGraph
} }
/// <inheritdoc/> /// <inheritdoc/>
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 // TODO: Probably need to introduce some kind of locking mechanism in the case of
// WriteableBitmap. // WriteableBitmap.
@ -89,6 +86,6 @@ namespace Avalonia.Rendering.SceneGraph
} }
/// <inheritdoc/> /// <inheritdoc/>
public bool HitTest(Point p) => Bounds.Contains(p); public override bool HitTest(Point p) => Bounds.Contains(p);
} }
} }

5
src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs

@ -28,8 +28,8 @@ namespace Avalonia.Rendering.SceneGraph
Point p1, Point p1,
Point p2, Point p2,
IDictionary<IVisual, Scene> childScenes = null) IDictionary<IVisual, Scene> childScenes = null)
: base(new Rect(p1, p2), transform, pen)
{ {
Bounds = new Rect(p1, p2).TransformToAABB(transform).Inflate(pen?.Thickness ?? 0);
Transform = transform; Transform = transform;
Pen = pen?.ToImmutable(); Pen = pen?.ToImmutable();
P1 = p1; P1 = p1;
@ -37,9 +37,6 @@ namespace Avalonia.Rendering.SceneGraph
ChildScenes = childScenes; ChildScenes = childScenes;
} }
/// <inheritdoc/>
public override Rect Bounds { get; }
/// <summary> /// <summary>
/// Gets the transform with which the node will be drawn. /// Gets the transform with which the node will be drawn.
/// </summary> /// </summary>

5
src/Avalonia.Visuals/Rendering/SceneGraph/OpacityMaskNode.cs

@ -19,6 +19,7 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="bounds">The bounds of the mask.</param> /// <param name="bounds">The bounds of the mask.</param>
/// <param name="childScenes">Child scenes for drawing visual brushes.</param> /// <param name="childScenes">Child scenes for drawing visual brushes.</param>
public OpacityMaskNode(IBrush mask, Rect bounds, IDictionary<IVisual, Scene> childScenes = null) public OpacityMaskNode(IBrush mask, Rect bounds, IDictionary<IVisual, Scene> childScenes = null)
: base(Rect.Empty, Matrix.Identity, null)
{ {
Mask = mask?.ToImmutable(); Mask = mask?.ToImmutable();
MaskBounds = bounds; MaskBounds = bounds;
@ -30,12 +31,10 @@ namespace Avalonia.Rendering.SceneGraph
/// opacity mask pop. /// opacity mask pop.
/// </summary> /// </summary>
public OpacityMaskNode() public OpacityMaskNode()
: base(Rect.Empty, Matrix.Identity, null)
{ {
} }
/// <inheritdoc/>
public override Rect Bounds => Rect.Empty;
/// <summary> /// <summary>
/// Gets the mask to be pushed or null if the operation represents a pop. /// Gets the mask to be pushed or null if the operation represents a pop.
/// </summary> /// </summary>

5
src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs

@ -30,8 +30,8 @@ namespace Avalonia.Rendering.SceneGraph
Rect rect, Rect rect,
float cornerRadius, float cornerRadius,
IDictionary<IVisual, Scene> childScenes = null) IDictionary<IVisual, Scene> childScenes = null)
: base(rect, transform, pen)
{ {
Bounds = rect.TransformToAABB(transform).Inflate(pen?.Thickness ?? 0);
Transform = transform; Transform = transform;
Brush = brush?.ToImmutable(); Brush = brush?.ToImmutable();
Pen = pen?.ToImmutable(); Pen = pen?.ToImmutable();
@ -40,9 +40,6 @@ namespace Avalonia.Rendering.SceneGraph
ChildScenes = childScenes; ChildScenes = childScenes;
} }
/// <inheritdoc/>
public override Rect Bounds { get; }
/// <summary> /// <summary>
/// Gets the transform with which the node will be drawn. /// Gets the transform with which the node will be drawn.
/// </summary> /// </summary>

5
src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs

@ -28,8 +28,8 @@ namespace Avalonia.Rendering.SceneGraph
Point origin, Point origin,
IFormattedTextImpl text, IFormattedTextImpl text,
IDictionary<IVisual, Scene> childScenes = null) IDictionary<IVisual, Scene> childScenes = null)
: base(new Rect(origin, text.Size), transform, null)
{ {
Bounds = new Rect(origin, text.Size).TransformToAABB(transform);
Transform = transform; Transform = transform;
Foreground = foreground?.ToImmutable(); Foreground = foreground?.ToImmutable();
Origin = origin; Origin = origin;
@ -37,9 +37,6 @@ namespace Avalonia.Rendering.SceneGraph
ChildScenes = childScenes; ChildScenes = childScenes;
} }
/// <inheritdoc/>
public override Rect Bounds { get; }
/// <summary> /// <summary>
/// Gets the transform with which the node will be drawn. /// Gets the transform with which the node will be drawn.
/// </summary> /// </summary>

34
src/Gtk/Avalonia.Gtk3/FramebufferManager.cs

@ -5,6 +5,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls.Platform.Surfaces; using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Threading;
namespace Avalonia.Gtk3 namespace Avalonia.Gtk3
{ {
@ -27,7 +28,38 @@ namespace Avalonia.Gtk3
var s = _window.ClientSize; var s = _window.ClientSize;
var width = (int) s.Width; var width = (int) s.Width;
var height = (int) s.Height; 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);
} }
} }
} }

5
src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -22,11 +23,15 @@ namespace Avalonia.Gtk3
internal static readonly MouseDevice Mouse = new MouseDevice(); internal static readonly MouseDevice Mouse = new MouseDevice();
internal static readonly KeyboardDevice Keyboard = new KeyboardDevice(); internal static readonly KeyboardDevice Keyboard = new KeyboardDevice();
internal static IntPtr App { get; set; } internal static IntPtr App { get; set; }
internal static string DisplayClassName;
public static bool UseDeferredRendering = true; public static bool UseDeferredRendering = true;
public static void Initialize() public static void Initialize()
{ {
Resolver.Resolve(); Resolver.Resolve();
Native.GtkInit(0, IntPtr.Zero); 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())) using (var utf = new Utf8Buffer("avalonia.app." + Guid.NewGuid()))
App = Native.GtkApplicationNew(utf, 0); App = Native.GtkApplicationNew(utf, 0);
//Mark current thread as UI thread //Mark current thread as UI thread

2
src/Gtk/Avalonia.Gtk3/IDeferredRenderOperation.cs

@ -4,6 +4,6 @@ namespace Avalonia.Gtk3
{ {
public interface IDeferredRenderOperation : IDisposable public interface IDeferredRenderOperation : IDisposable
{ {
void RenderNow(); void RenderNow(IntPtr? ctx);
} }
} }

31
src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs

@ -16,23 +16,23 @@ namespace Avalonia.Gtk3
{ {
private readonly WindowBaseImpl _impl; private readonly WindowBaseImpl _impl;
private readonly GtkWidget _widget; private readonly GtkWidget _widget;
private CairoSurface _surface; private ManagedCairoSurface _surface;
private int _factor; private int _factor;
private object _lock = new object(); 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; _impl = impl;
_widget = impl.GtkWidget; _widget = impl.GtkWidget;
_factor = (int)(Native.GtkWidgetGetScaleFactor?.Invoke(_widget) ?? 1u); _factor = factor;
width *= _factor; width *= _factor;
height *= _factor; height *= _factor;
_surface = Native.CairoImageSurfaceCreate(1, width, height); _surface = new ManagedCairoSurface(width, height);
Width = width; Width = width;
Height = height; Height = height;
Address = Native.CairoImageSurfaceGetData(_surface); Address = _surface.Buffer;
RowBytes = Native.CairoImageSurfaceGetStride(_surface); RowBytes = _surface.Stride;
Native.CairoSurfaceFlush(_surface); Native.CairoSurfaceFlush(_surface.Surface);
} }
static void Draw(IntPtr context, CairoSurface surface, double factor) static void Draw(IntPtr context, CairoSurface surface, double factor)
@ -83,15 +83,15 @@ namespace Avalonia.Gtk3
class RenderOp : IDeferredRenderOperation class RenderOp : IDeferredRenderOperation
{ {
private readonly GtkWidget _widget; private readonly GtkWidget _widget;
private CairoSurface _surface; private ManagedCairoSurface _surface;
private readonly double _factor; private readonly double _factor;
private readonly int _width; private readonly int _width;
private readonly int _height; 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; _widget = widget;
this._surface = _surface; _surface = surface ?? throw new ArgumentNullException();
_factor = factor; _factor = factor;
_width = width; _width = width;
_height = height; _height = height;
@ -103,9 +103,12 @@ namespace Avalonia.Gtk3
_surface = null; _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 (Dispatcher.UIThread.CheckAccess())
{ {
if (_impl.CurrentCairoContext != IntPtr.Zero) if (_impl.CurrentCairoContext != IntPtr.Zero)
Draw(_impl.CurrentCairoContext, _surface, _factor); Draw(_impl.CurrentCairoContext, _surface.Surface, _factor);
else else
DrawToWidget(_widget, _surface, Width, Height, _factor); DrawToWidget(_widget, _surface.Surface, Width, Height, _factor);
_surface.Dispose(); _surface.Dispose();
} }
else else

2
src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs

@ -53,7 +53,7 @@ namespace Avalonia.Gtk3.Interop
if (interval == 0) if (interval == 0)
throw new ArgumentException("Don't know how to create a timer with zero or negative interval"); throw new ArgumentException("Don't know how to create a timer with zero or negative interval");
var timer = new Timer (); var timer = new Timer ();
GlibTimeout.Add(GlibPriority.FromDispatcherPriority(DispatcherPriority.Background), interval, GlibTimeout.Add(GlibPriority.FromDispatcherPriority(DispatcherPriority.Normal), interval,
() => () =>
{ {
if (timer.Stopped) if (timer.Stopped)

38
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<IRuntimePlatform>();
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;
}
}
}
}

34
src/Gtk/Avalonia.Gtk3/Interop/Native.cs

@ -160,6 +160,9 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
public delegate CairoSurface cairo_image_surface_create(int format, int width, int height); 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)] [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
public delegate IntPtr cairo_image_surface_get_data(CairoSurface surface); public delegate IntPtr cairo_image_surface_get_data(CairoSurface surface);
@ -178,7 +181,7 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
public delegate void cairo_surface_destroy(IntPtr surface); public delegate void cairo_surface_destroy(IntPtr surface);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
public delegate void cairo_set_source_surface(IntPtr cr, CairoSurface surface, double x, double y); 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)] [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
public delegate GdkWindowState gdk_window_get_state(IntPtr window); public delegate GdkWindowState gdk_window_get_state(IntPtr window);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate void gdk_window_iconify(IntPtr window); public delegate void gtk_window_iconify(GtkWindow window);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate void gdk_window_deiconify(IntPtr window); public delegate void gtk_window_deiconify(GtkWindow window);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate void gdk_window_maximize(IntPtr window); public delegate void gtk_window_maximize(GtkWindow window);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate void gdk_window_unmaximize(IntPtr window); public delegate void gtk_window_unmaximize(GtkWindow window);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] [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); 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)] [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
public delegate void g_object_ref(GObject instance); 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)] [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
public delegate ulong g_signal_connect_object(GObject instance, Utf8Buffer signal, IntPtr handler, IntPtr userData, int flags); 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.gtk_dialog_add_button GtkDialogAddButton;
public static D.g_object_unref GObjectUnref; public static D.g_object_unref GObjectUnref;
public static D.g_object_ref GObjectRef; 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_connect_object GSignalConnectObject;
public static D.g_signal_handler_disconnect GSignalHandlerDisconnect; public static D.g_signal_handler_disconnect GSignalHandlerDisconnect;
public static D.g_timeout_add GTimeoutAdd; 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_origin GdkWindowGetOrigin;
public static D.gdk_window_get_pointer GdkWindowGetPointer; public static D.gdk_window_get_pointer GdkWindowGetPointer;
public static D.gdk_window_get_state GdkWindowGetState; public static D.gdk_window_get_state GdkWindowGetState;
public static D.gdk_window_iconify GdkWindowIconify; public static D.gtk_window_iconify GtkWindowIconify;
public static D.gdk_window_deiconify GdkWindowDeiconify; public static D.gtk_window_deiconify GtkWindowDeiconify;
public static D.gdk_window_maximize GdkWindowMaximize; public static D.gtk_window_maximize GtkWindowMaximize;
public static D.gdk_window_unmaximize GdkWindowUnmaximize; public static D.gtk_window_unmaximize GtkWindowUnmaximize;
public static D.gdk_window_begin_move_drag GdkWindowBeginMoveDrag; public static D.gdk_window_begin_move_drag GdkWindowBeginMoveDrag;
public static D.gdk_window_begin_resize_drag GdkWindowBeginResizeDrag; public static D.gdk_window_begin_resize_drag GdkWindowBeginResizeDrag;
public static D.gdk_event_request_motions GdkEventRequestMotions; 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.gdk_cairo_create GdkCairoCreate;
public static D.cairo_image_surface_create CairoImageSurfaceCreate; 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_data CairoImageSurfaceGetData;
public static D.cairo_image_surface_get_stride CairoImageSurfaceGetStride; public static D.cairo_image_surface_get_stride CairoImageSurfaceGetStride;
public static D.cairo_surface_mark_dirty CairoSurfaceMarkDirty; public static D.cairo_surface_mark_dirty CairoSurfaceMarkDirty;

47
src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs

@ -4,6 +4,7 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Gtk3.Interop; using Avalonia.Gtk3.Interop;
using Avalonia.Input; using Avalonia.Input;
@ -29,7 +30,8 @@ namespace Avalonia.Gtk3
private GCHandle _gcHandle; private GCHandle _gcHandle;
private object _lock = new object(); private object _lock = new object();
private IDeferredRenderOperation _nextRenderOperation; private IDeferredRenderOperation _nextRenderOperation;
private readonly AutoResetEvent _canSetNextOperation = new AutoResetEvent(true);
internal IntPtr? GdkWindowHandle;
public WindowBaseImpl(GtkWindow gtkWidget) public WindowBaseImpl(GtkWindow gtkWidget)
{ {
@ -53,12 +55,8 @@ namespace Avalonia.Gtk3
ConnectEvent("leave-notify-event", OnLeaveNotifyEvent); ConnectEvent("leave-notify-event", OnLeaveNotifyEvent);
Connect<Native.D.signal_generic>("destroy", OnDestroy); Connect<Native.D.signal_generic>("destroy", OnDestroy);
Native.GtkWidgetRealize(gtkWidget); Native.GtkWidgetRealize(gtkWidget);
GdkWindowHandle = this.Handle.Handle;
_lastSize = ClientSize; _lastSize = ClientSize;
GlibTimeout.Add(0, 16, () =>
{
Invalidate(default(Rect));
return true;
});
if (Gtk3Platform.UseDeferredRendering) if (Gtk3Platform.UseDeferredRendering)
{ {
Native.GtkWidgetSetDoubleBuffered(gtkWidget, false); Native.GtkWidgetSetDoubleBuffered(gtkWidget, false);
@ -138,7 +136,7 @@ namespace Avalonia.Gtk3
? RawMouseEventType.LeftButtonDown ? RawMouseEventType.LeftButtonDown
: evnt->button == 3 ? RawMouseEventType.RightButtonDown : RawMouseEventType.MiddleButtonDown, : evnt->button == 3 ? RawMouseEventType.RightButtonDown : RawMouseEventType.MiddleButtonDown,
new Point(evnt->x, evnt->y), GetModifierKeys(evnt->state)); new Point(evnt->x, evnt->y), GetModifierKeys(evnt->state));
Input?.Invoke(e); OnInput(e);
return true; return true;
} }
@ -166,7 +164,7 @@ namespace Avalonia.Gtk3
_inputRoot, _inputRoot,
RawMouseEventType.Move, RawMouseEventType.Move,
position, GetModifierKeys(evnt->state)); position, GetModifierKeys(evnt->state));
Input(e); OnInput(e);
return true; return true;
} }
@ -195,7 +193,7 @@ namespace Avalonia.Gtk3
} }
var e = new RawMouseWheelEventArgs(Gtk3Platform.Mouse, evnt->time, _inputRoot, var e = new RawMouseWheelEventArgs(Gtk3Platform.Mouse, evnt->time, _inputRoot,
new Point(evnt->x, evnt->y), delta, GetModifierKeys(evnt->state)); new Point(evnt->x, evnt->y), delta, GetModifierKeys(evnt->state));
Input(e); OnInput(e);
return true; return true;
} }
@ -210,7 +208,7 @@ namespace Avalonia.Gtk3
evnt->time, evnt->time,
evnt->type == GdkEventType.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp, evnt->type == GdkEventType.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
Avalonia.Gtk.Common.KeyTransform.ConvertKey((GdkKey)evnt->keyval), GetModifierKeys((GdkModifierType)evnt->state)); Avalonia.Gtk.Common.KeyTransform.ConvertKey((GdkKey)evnt->keyval), GetModifierKeys((GdkModifierType)evnt->state));
Input(e); OnInput(e);
return true; return true;
} }
@ -218,7 +216,7 @@ namespace Avalonia.Gtk3
{ {
var evnt = (GdkEventCrossing*) pev; var evnt = (GdkEventCrossing*) pev;
var position = new Point(evnt->x, evnt->y); var position = new Point(evnt->x, evnt->y);
Input(new RawMouseEventArgs(Gtk3Platform.Mouse, OnInput(new RawMouseEventArgs(Gtk3Platform.Mouse,
evnt->time, evnt->time,
_inputRoot, _inputRoot,
RawMouseEventType.Move, RawMouseEventType.Move,
@ -228,7 +226,7 @@ namespace Avalonia.Gtk3
private unsafe bool OnCommit(IntPtr gtkwidget, IntPtr utf8string, IntPtr userdata) 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; return true;
} }
@ -260,11 +258,19 @@ namespace Avalonia.Gtk3
public void SetNextRenderOperation(IDeferredRenderOperation op) public void SetNextRenderOperation(IDeferredRenderOperation op)
{ {
lock (_lock) while (true)
{ {
_nextRenderOperation?.Dispose(); lock (_lock)
_nextRenderOperation = op; {
if (_nextRenderOperation == null)
{
_nextRenderOperation = op;
return;
}
}
_canSetNextOperation.WaitOne();
} }
} }
private void OnRenderTick() private void OnRenderTick()
@ -277,10 +283,11 @@ namespace Avalonia.Gtk3
op = _nextRenderOperation; op = _nextRenderOperation;
_nextRenderOperation = null; _nextRenderOperation = null;
} }
_canSetNextOperation.Set();
} }
if (op != null) if (op != null)
{ {
op?.RenderNow(); op?.RenderNow(null);
op?.Dispose(); op?.Dispose();
} }
} }
@ -311,7 +318,7 @@ namespace Avalonia.Gtk3
public IMouseDevice MouseDevice => Gtk3Platform.Mouse; 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; public IPlatformHandle Handle => this;
@ -338,6 +345,11 @@ namespace Avalonia.Gtk3
public void SetInputRoot(IInputRoot inputRoot) => _inputRoot = inputRoot; public void SetInputRoot(IInputRoot inputRoot) => _inputRoot = inputRoot;
void OnInput(RawInputEventArgs args)
{
Dispatcher.UIThread.InvokeAsync(() => Input?.Invoke(args), DispatcherPriority.Input);
}
public Point PointToClient(Point point) public Point PointToClient(Point point)
{ {
int x, y; int x, y;
@ -387,6 +399,7 @@ namespace Avalonia.Gtk3
public Size ClientSize { get; private set; } public Size ClientSize { get; private set; }
public int LastKnownScaleFactor { get; private set; }
public void Resize(Size value) public void Resize(Size value)
{ {

9
src/Gtk/Avalonia.Gtk3/WindowImpl.cs

@ -32,15 +32,14 @@ namespace Avalonia.Gtk3
} }
set set
{ {
var w = Native.GtkWidgetGetWindow(GtkWidget);
if (value == WindowState.Minimized) if (value == WindowState.Minimized)
Native.GdkWindowIconify(w); Native.GtkWindowIconify(GtkWidget);
else if (value == WindowState.Maximized) else if (value == WindowState.Maximized)
Native.GdkWindowMaximize(w); Native.GtkWindowMaximize(GtkWidget);
else else
{ {
Native.GdkWindowUnmaximize(w); Native.GtkWindowUnmaximize(GtkWidget);
Native.GdkWindowDeiconify(w); Native.GtkWindowDeiconify(GtkWidget);
} }
} }
} }

54
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];
}
}
}

55
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<IRuntimePlatform>().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; }
}
}

142
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. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System; using System;
using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using System.Resources; using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using Avalonia.Platform; using Avalonia.Platform;
@ -11,20 +14,145 @@ namespace Avalonia.Shared.PlatformSupport
{ {
internal partial class StandardRuntimePlatform : IRuntimePlatform 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); public void PostThreadPoolItem(Action cb) => ThreadPool.UnsafeQueueUserWorkItem(_ => cb(), null);
#endif public Assembly[] GetLoadedAssemblies() => AppDomain.CurrentDomain.GetAssemblies();
public IDisposable StartSystemTimer(TimeSpan interval, Action tick) public IDisposable StartSystemTimer(TimeSpan interval, Action tick)
{ {
return new Timer(_ => tick(), null, interval, interval); 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<string> Backtraces = new List<string>();
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
} }
} }

23
src/Skia/Avalonia.Skia/BitmapImpl.cs

@ -20,16 +20,35 @@ namespace Avalonia.Skia
_dpi = new Vector(96, 96); _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) public BitmapImpl(int width, int height, Vector dpi, PixelFormat? fmt = null)
{ {
PixelHeight = height; PixelHeight = height;
PixelWidth = width; PixelWidth = width;
_dpi = dpi; _dpi = dpi;
var colorType = fmt?.ToSkColorType() ?? SKImageInfo.PlatformColorType; var colorType = fmt?.ToSkColorType() ?? SKImageInfo.PlatformColorType;
var runtime = AvaloniaLocator.Current?.GetService<IRuntimePlatform>()?.GetRuntimeInfo(); var runtimePlatform = AvaloniaLocator.Current?.GetService<IRuntimePlatform>();
var runtime = runtimePlatform?.GetRuntimeInfo();
if (runtime?.IsDesktop == true && runtime?.OperatingSystem == OperatingSystemType.Linux) if (runtime?.IsDesktop == true && runtime?.OperatingSystem == OperatingSystemType.Linux)
colorType = SKColorType.Bgra8888; 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<IRuntimePlatform>();
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); Bitmap.Erase(SKColor.Empty);
} }

9
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -45,7 +45,7 @@ namespace Avalonia.Skia
var s = sourceRect.ToSKRect(); var s = sourceRect.ToSKRect();
var d = destRect.ToSKRect(); var d = destRect.ToSKRect();
using (var paint = new SKPaint() 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); Canvas.DrawBitmap(impl.Bitmap, s, d, paint);
} }
@ -112,6 +112,7 @@ namespace Avalonia.Skia
public readonly SKPaint Paint; public readonly SKPaint Paint;
private IDisposable _disposable1; private IDisposable _disposable1;
private IDisposable _disposable2;
public IDisposable ApplyTo(SKPaint paint) public IDisposable ApplyTo(SKPaint paint)
{ {
@ -127,6 +128,8 @@ namespace Avalonia.Skia
{ {
if (_disposable1 == null) if (_disposable1 == null)
_disposable1 = disposable; _disposable1 = disposable;
else if (_disposable2 == null)
_disposable2 = disposable;
else else
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
@ -135,12 +138,14 @@ namespace Avalonia.Skia
{ {
Paint = paint; Paint = paint;
_disposable1 = null; _disposable1 = null;
_disposable2 = null;
} }
public void Dispose() public void Dispose()
{ {
Paint?.Dispose(); Paint?.Dispose();
_disposable1?.Dispose(); _disposable1?.Dispose();
_disposable2?.Dispose();
} }
} }
@ -221,8 +226,8 @@ namespace Avalonia.Skia
_visualBrushRenderer.RenderVisualBrush(ctx, visualBrush); _visualBrushRenderer.RenderVisualBrush(ctx, visualBrush);
} }
rv.AddDisposable(tileBrushImage);
tileBrushImage = intermediate; tileBrushImage = intermediate;
rv.AddDisposable(tileBrushImage);
} }
} }
else else

2
src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs

@ -146,6 +146,8 @@ namespace Avalonia.Direct2D1
} }
if (s is IExternalDirect2DRenderTargetSurface external) if (s is IExternalDirect2DRenderTargetSurface external)
return new ExternalRenderTarget(external, s_dwfactory); 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"); throw new NotSupportedException("Don't know how to create a Direct2D1 renderer from any of provided surfaces");
} }

84
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();
});
}
}
}
}

13
src/Windows/Avalonia.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs

@ -22,8 +22,9 @@ namespace Avalonia.Direct2D1.Media
int width, int width,
int height, int height,
double dpiX, double dpiX,
double dpiY) double dpiY,
: base(imagingFactory, width, height) Platform.PixelFormat? pixelFormat = null)
: base(imagingFactory, width, height, pixelFormat)
{ {
var props = new RenderTargetProperties var props = new RenderTargetProperties
{ {
@ -45,9 +46,13 @@ namespace Avalonia.Direct2D1.Media
base.Dispose(); 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);
} }
} }
} }

1
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) 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); WicImpl = new Bitmap(factory, width, height, format.ToWic(), BitmapCreateCacheOption.CacheOnDemand);
_factory = factory;
PixelFormat = format; PixelFormat = format;
using (var l = WicImpl.Lock(BitmapLockFlags.Write)) using (var l = WicImpl.Lock(BitmapLockFlags.Write))
{ {

2
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs

@ -187,7 +187,7 @@ namespace Avalonia.Base.UnitTests
source.OnNext(45); source.OnNext(45);
Assert.Equal(null, target.Foo); Assert.Null(target.Foo);
} }
[Fact] [Fact]

2
tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs

@ -66,7 +66,7 @@ namespace Avalonia.Controls.UnitTests.Generators
target.Dematerialize(1, 1); target.Dematerialize(1, 1);
Assert.Equal(containers[0].ContainerControl, target.ContainerFromIndex(0)); 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)); Assert.Equal(containers[2].ContainerControl, target.ContainerFromIndex(2));
} }

4
tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs

@ -107,7 +107,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.Content = child; target.Content = child;
target.Content = null; target.Content = null;
Assert.Equal(null, child.GetLogicalParent()); Assert.Null(child.GetLogicalParent());
Assert.Empty(target.GetLogicalChildren()); Assert.Empty(target.GetLogicalChildren());
} }
@ -120,7 +120,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.Content = child; target.Content = child;
target.Content = null; target.Content = null;
Assert.Equal(null, child.GetVisualParent()); Assert.Null(child.GetVisualParent());
Assert.Empty(target.GetVisualChildren()); Assert.Empty(target.GetVisualChildren());
} }

2
tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs

@ -191,7 +191,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.Content = "bar"; target.Content = "bar";
target.UpdateChild(); target.UpdateChild();
Assert.Equal(null, foo.Parent); Assert.Null(foo.Parent);
logicalChildren = target.GetLogicalChildren(); logicalChildren = target.GetLogicalChildren();

8
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs

@ -171,7 +171,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.SelectedItem = new Item(); target.SelectedItem = new Item();
Assert.Equal(null, target.SelectedItem); Assert.Null(target.SelectedItem);
Assert.Equal(-1, target.SelectedIndex); Assert.Equal(-1, target.SelectedIndex);
} }
@ -278,7 +278,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.Items = null; target.Items = null;
Assert.Equal(null, target.SelectedItem); Assert.Null(target.SelectedItem);
Assert.Equal(-1, target.SelectedIndex); Assert.Equal(-1, target.SelectedIndex);
} }
@ -305,7 +305,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
items.RemoveAt(1); items.RemoveAt(1);
Assert.Equal(null, target.SelectedItem); Assert.Null(target.SelectedItem);
Assert.Equal(-1, target.SelectedIndex); Assert.Equal(-1, target.SelectedIndex);
} }
@ -334,7 +334,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
items.Clear(); items.Clear();
Assert.Equal(null, target.SelectedItem); Assert.Null(target.SelectedItem);
Assert.Equal(-1, target.SelectedIndex); Assert.Equal(-1, target.SelectedIndex);
} }

2
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs

@ -76,7 +76,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.SelectedItems = new AvaloniaList<object>(); target.SelectedItems = new AvaloniaList<object>();
Assert.Equal(-1, target.SelectedIndex); Assert.Equal(-1, target.SelectedIndex);
Assert.Equal(null, target.SelectedItem); Assert.Null(target.SelectedItem);
} }
[Fact] [Fact]

4
tests/Avalonia.Controls.UnitTests/StackPanelTests.cs

@ -70,7 +70,7 @@ namespace Avalonia.Controls.UnitTests
target.Measure(Size.Infinity); target.Measure(Size.Infinity);
target.Arrange(new Rect(target.DesiredSize)); target.Arrange(new Rect(target.DesiredSize));
Assert.Equal(new Size(120, 130), target.Bounds.Size); Assert.Equal(new Size(120, 120), target.Bounds.Size);
Assert.Equal(new Rect(0, 0, 120, 20), target.Children[0].Bounds); Assert.Equal(new Rect(0, 0, 120, 20), target.Children[0].Bounds);
Assert.Equal(new Rect(0, 30, 120, 30), target.Children[1].Bounds); Assert.Equal(new Rect(0, 30, 120, 30), target.Children[1].Bounds);
Assert.Equal(new Rect(0, 70, 120, 50), target.Children[2].Bounds); Assert.Equal(new Rect(0, 70, 120, 50), target.Children[2].Bounds);
@ -94,7 +94,7 @@ namespace Avalonia.Controls.UnitTests
target.Measure(Size.Infinity); target.Measure(Size.Infinity);
target.Arrange(new Rect(target.DesiredSize)); target.Arrange(new Rect(target.DesiredSize));
Assert.Equal(new Size(130, 120), target.Bounds.Size); Assert.Equal(new Size(120, 120), target.Bounds.Size);
Assert.Equal(new Rect(0, 0, 20, 120), target.Children[0].Bounds); Assert.Equal(new Rect(0, 0, 20, 120), target.Children[0].Bounds);
Assert.Equal(new Rect(30, 0, 30, 120), target.Children[1].Bounds); Assert.Equal(new Rect(30, 0, 30, 120), target.Children[1].Bounds);
Assert.Equal(new Rect(70, 0, 50, 120), target.Children[2].Bounds); Assert.Equal(new Rect(70, 0, 50, 120), target.Children[2].Bounds);

2
tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_RelativeSource.cs

@ -117,7 +117,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
}; };
target.Bind(TextBox.TextProperty, binding); target.Bind(TextBox.TextProperty, binding);
Assert.Equal(null, target.Text); Assert.Null(target.Text);
} }
[Fact] [Fact]

4
tests/Avalonia.Markup.Xaml.UnitTests/Templates/MemberSelectorTests.cs

@ -63,7 +63,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Templates
{ {
var selector = new MemberSelector() { MemberName = "StringValue" }; var selector = new MemberSelector() { MemberName = "StringValue" };
Assert.Equal(null, selector.Select(null)); Assert.Null(selector.Select(null));
} }
[Fact] [Fact]
@ -73,7 +73,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Templates
var data = new Item() { StringValue = "Value1" }; var data = new Item() { StringValue = "Value1" };
Assert.Same(null, selector.Select(data)); Assert.Null(selector.Select(data));
} }
[Fact] [Fact]

11
tests/Avalonia.RenderTests/Media/BitmapTests.cs

@ -64,13 +64,13 @@ namespace Avalonia.Direct2D1.RenderTests.Media
public void Deallocate() => Marshal.FreeHGlobal(Address); public void Deallocate() => Marshal.FreeHGlobal(Address);
} }
#if AVALONIA_SKIA
[Theory] [Theory]
#else [InlineData(PixelFormat.Rgba8888), InlineData(PixelFormat.Bgra8888),
[Theory(Skip = "Framebuffer not supported")] #if SKIA
InlineData(PixelFormat.Rgb565)
#endif #endif
[InlineData(PixelFormat.Rgba8888), InlineData(PixelFormat.Bgra8888), InlineData(PixelFormat.Rgb565)] ]
public void FramebufferRenderResultsShouldBeUsableAsBitmap(PixelFormat fmt) public void FramebufferRenderResultsShouldBeUsableAsBitmap(PixelFormat fmt)
{ {
var testName = nameof(FramebufferRenderResultsShouldBeUsableAsBitmap) + "_" + fmt; var testName = nameof(FramebufferRenderResultsShouldBeUsableAsBitmap) + "_" + fmt;
@ -84,6 +84,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media
ctx.FillRectangle(Brushes.Chartreuse, new Rect(0, 0, 20, 100)); ctx.FillRectangle(Brushes.Chartreuse, new Rect(0, 0, 20, 100));
ctx.FillRectangle(Brushes.Crimson, new Rect(20, 0, 20, 100)); ctx.FillRectangle(Brushes.Crimson, new Rect(20, 0, 20, 100));
ctx.FillRectangle(Brushes.Gold, new Rect(40, 0, 20, 100)); ctx.FillRectangle(Brushes.Gold, new Rect(40, 0, 20, 100));
ctx.PopOpacity();
} }
var bmp = new Bitmap(fmt, fb.Address, fb.Width, fb.Height, fb.RowBytes); var bmp = new Bitmap(fmt, fb.Address, fb.Width, fb.Height, fb.RowBytes);

55
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs

@ -0,0 +1,55 @@
using System;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering.SceneGraph;
using Xunit;
namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
{
public class DrawOperationTests
{
[Fact]
public void Empty_Bounds_Remain_Empty()
{
var target = new TestDrawOperation(Rect.Empty, Matrix.Identity, null);
Assert.Equal(Rect.Empty, target.Bounds);
}
[Theory]
[InlineData(10, 10, 10, 10, 1, 1, 1, 9, 9, 12, 12)]
[InlineData(10, 10, 10, 10, 1, 1, 2, 9, 9, 12, 12)]
[InlineData(10, 10, 10, 10, 1.5, 1.5, 1, 14, 14, 17, 17)]
public void Rectangle_Bounds_Are_Snapped_To_Pixels(
double x,
double y,
double width,
double height,
double scaleX,
double scaleY,
double? penThickness,
double expectedX,
double expectedY,
double expectedWidth,
double expectedHeight)
{
var target = new TestDrawOperation(
new Rect(x, y, width, height),
Matrix.CreateScale(scaleX, scaleY),
penThickness.HasValue ? new Pen(Brushes.Black, penThickness.Value) : null);
Assert.Equal(new Rect(expectedX, expectedY, expectedWidth, expectedHeight), target.Bounds);
}
private class TestDrawOperation : DrawOperation
{
public TestDrawOperation(Rect bounds, Matrix transform, Pen pen)
:base(bounds, transform, pen)
{
}
public override bool HitTest(Point p) => false;
public override void Render(IDrawingContextImpl context) { }
}
}
}

BIN
tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Bgra8888.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 800 B

BIN
tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Rgb565.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 786 B

BIN
tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Rgba8888.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 800 B

Loading…
Cancel
Save