Browse Source

Merge branch 'master' into scenegraph

Conflicts:
	src/Avalonia.Visuals/Media/FormattedTextStyleSpan.cs
	src/Avalonia.Visuals/Media/Typeface.cs
	src/Avalonia.Visuals/Rendering/RendererMixin.cs
	src/Gtk/Avalonia.Cairo/Media/StreamGeometryImpl.cs
	src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
	tests/Avalonia.Input.UnitTests/InputElement_HitTesting.cs
	tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs
	tests/Avalonia.UnitTests/TestServices.cs
	tests/Avalonia.Visuals.UnitTests/Media/TypefaceTests.cs
scenegraph-after-breakage
Steven Kirk 9 years ago
parent
commit
ae7cf4a6ef
  1. 5
      .ncrunch/Avalonia.LinuxFramebuffer.v3.ncrunchproject
  2. 47
      Avalonia.sln
  3. 3
      appveyor.yml
  4. 2
      build.cake
  5. 22
      packages.cake
  6. 2
      readme.md
  7. 1
      samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
  8. 20
      samples/ControlCatalog.NetCore/Program.cs
  9. 2
      src/Avalonia.Controls/Button.cs
  10. 6
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  11. 2
      src/Avalonia.Controls/Primitives/Thumb.cs
  12. 2
      src/Avalonia.Controls/TextBlock.cs
  13. 2
      src/Avalonia.Controls/TextBox.cs
  14. 4
      src/Avalonia.Controls/Window.cs
  15. 2
      src/Avalonia.HtmlRenderer/HtmlControl.cs
  16. 2
      src/Avalonia.Input/InputElement.cs
  17. 20
      src/Avalonia.Visuals/Media/FormattedTextStyleSpan.cs
  18. 26
      src/Avalonia.Visuals/Media/Typeface.cs
  19. 2
      src/Gtk/Avalonia.Cairo/Media/StreamGeometryImpl.cs
  20. 14
      src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj
  21. 89
      src/Linux/Avalonia.LinuxFramebuffer/EvDevDevice.cs
  22. 68
      src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
  23. 138
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebuffer.cs
  24. 75
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  25. 47
      src/Linux/Avalonia.LinuxFramebuffer/LockedFramebuffer.cs
  26. 120
      src/Linux/Avalonia.LinuxFramebuffer/Mice.cs
  27. 254
      src/Linux/Avalonia.LinuxFramebuffer/NativeUnsafeMethods.cs
  28. 112
      src/Linux/Avalonia.LinuxFramebuffer/PlatformThreadingInterface.cs
  29. 21
      src/Linux/Avalonia.LinuxFramebuffer/Stubs.cs
  30. 26
      src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
  31. 4
      tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs
  32. 15
      tests/Avalonia.UnitTests/TestServices.cs
  33. 4
      tests/Avalonia.Visuals.UnitTests/Media/TypefaceTests.cs

5
.ncrunch/Avalonia.LinuxFramebuffer.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings>
</ProjectConfiguration>

47
Avalonia.sln

@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.9
VisualStudioVersion = 15.0.26228.4
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Base", "src\Avalonia.Base\Avalonia.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}"
EndProject
@ -185,6 +185,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6F
build\UnitTests.NetCore.targets = build\UnitTests.NetCore.targets
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Linux", "Linux", "{86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.LinuxFramebuffer", "src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj", "{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Skia\Avalonia.Skia\Avalonia.Skia.projitems*{2f59f3d0-748d-4652-b01e-e0d954756308}*SharedItemsImports = 13
@ -2503,6 +2507,46 @@ Global
{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|Mono.Build.0 = Release|Any CPU
{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|x86.ActiveCfg = Release|Any CPU
{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|x86.Build.0 = Release|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|Mono.ActiveCfg = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|Mono.Build.0 = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|iPhone.Build.0 = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|Mono.ActiveCfg = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|Mono.Build.0 = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|x86.ActiveCfg = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|x86.Build.0 = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|iPhone.Build.0 = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|Mono.ActiveCfg = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|Mono.Build.0 = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|x86.ActiveCfg = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|x86.Build.0 = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|Any CPU.Build.0 = Release|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|iPhone.ActiveCfg = Release|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|iPhone.Build.0 = Release|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|Mono.ActiveCfg = Release|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|Mono.Build.0 = Release|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|x86.ActiveCfg = Release|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -2561,5 +2605,6 @@ Global
{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A} = {74487168-7D91-487E-BF93-055F2251461E}
{4D6FAF79-58B4-482F-9122-0668C346364C} = {74487168-7D91-487E-BF93-055F2251461E}
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
EndGlobalSection
EndGlobal

3
appveyor.yml

@ -1,6 +1,7 @@
os: Visual Studio 2017
os: Previous Visual Studio 2017
platform:
- Any CPU
skip_branch_with_pr: true
configuration:
- Release
environment:

2
build.cake

@ -4,7 +4,7 @@
#addin "nuget:?package=Polly&version=4.2.0"
#addin "nuget:?package=NuGet.Core&version=2.12.0"
#tool "nuget:https://dotnet.myget.org/F/nuget-build/?package=NuGet.CommandLine&version=4.3.0-beta1-2361&prerelease"
#tool "nuget:https://dotnet.myget.org/F/nuget-build/?package=NuGet.CommandLine&version=4.3.0-preview1-3980&prerelease"
#tool "nuget:?package=JetBrains.dotMemoryUnit&version=2.1.20150828.125449"
///////////////////////////////////////////////////////////////////////////////
// TOOLS

22
packages.cake

@ -198,7 +198,6 @@ public class Packages
new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp1.0", Version = "4.3.0" },
new NuSpecDependency() { Id = "Microsoft.Extensions.DependencyModel", TargetFramework = "netcoreapp1.0", Version = "1.1.0" },
new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netcoreapp1.0", Version = "1.6.0" },
new NuSpecDependency() { Id = "Microsoft.NETCore.Portable.Compatibility", TargetFramework = "netcoreapp1.0", Version = "1.0.1" },
new NuSpecDependency() { Id = "Splat", TargetFramework = "netcoreapp1.0", Version = SplatVersion },
new NuSpecDependency() { Id = "Serilog", TargetFramework = "netcoreapp1.0", Version = SerilogVersion },
new NuSpecDependency() { Id = "Sprache", TargetFramework = "netcoreapp1.0", Version = SpracheVersion },
@ -429,8 +428,7 @@ public class Packages
//netstandard1.3
new NuSpecDependency() { Id = "Avalonia", TargetFramework = "netstandard1.3", Version = parameters.Version },
new NuSpecDependency() { Id = "SkiaSharp", TargetFramework = "netstandard1.3", Version = SkiaSharpVersion },
new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netstandard1.3", Version = "1.6.0" },
new NuSpecDependency() { Id = "Microsoft.NETCore.Portable.Compatibility", TargetFramework = "netstandard1.3", Version = "1.0.1" }
new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netstandard1.3", Version = "1.6.0" }
},
Files = new []
{
@ -460,6 +458,24 @@ public class Packages
},
BasePath = context.Directory("./"),
OutputDirectory = parameters.NugetRoot
},
///////////////////////////////////////////////////////////////////////////////
// Avalonia.LinuxFramebuffer
///////////////////////////////////////////////////////////////////////////////
new NuGetPackSettings()
{
Id = "Avalonia.LinuxFramebuffer",
Dependencies = new []
{
new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", Version = parameters.Version }
},
Files = new []
{
new NuSpecContent { Source = "Avalonia.LinuxFramebuffer/bin/" + parameters.DirSuffix + "/netstandard1.3/Avalonia.LinuxFramebuffer.dll", Target = "lib/netstandard1.3" }
},
BasePath = context.Directory("./src/Linux/"),
OutputDirectory = parameters.NugetRoot
}
};

2
readme.md

@ -3,7 +3,7 @@
| Gitter Chat | Windows Build Status | Linux/Mac Build Status | Code Coverage |
|---|---|---|---|
| [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) | [![Build status](https://ci.appveyor.com/api/projects/status/hubk3k0w9idyibfg/branch/master?svg=true)](https://ci.appveyor.com/project/AvaloniaUI/Avalonia/branch/master) | [![Build Status](https://travis-ci.org/AvaloniaUI/Avalonia.svg?branch=master)](https://travis-ci.org/AvaloniaUI/Avalonia) | [![codecov](https://codecov.io/gh/AvaloniaUI/Avalonia/branch/master/graph/badge.svg)](https://codecov.io/gh/AvaloniaUI/Avalonia) |
| [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) | [![Build status](https://ci.appveyor.com/api/projects/status/hubk3k0w9idyibfg/branch/master?svg=true)](https://ci.appveyor.com/project/AvaloniaUI/Avalonia/branch/master) | [![Build Status](https://travis-ci.org/AvaloniaUI/Avalonia.svg?branch=master)](https://travis-ci.org/AvaloniaUI/Avalonia) | [![codecov](https://codecov.io/gh/AvaloniaUI/Avalonia/branch/master/graph/badge.svg)](https://codecov.io/gh/AvaloniaUI/Avalonia) |
A multi-platform .NET UI framework. It can run on Windows, Linux, Mac OS X, iOS and Android.

1
samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj

@ -7,6 +7,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.DotNetCoreRuntime\Avalonia.DotNetCoreRuntime.csproj" />
<ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
</ItemGroup>

20
samples/ControlCatalog.NetCore/Program.cs

@ -1,4 +1,5 @@
using System;
using System.Linq;
using Avalonia;
namespace ControlCatalog.NetCore
@ -7,9 +8,22 @@ namespace ControlCatalog.NetCore
{
static void Main(string[] args)
{
AppBuilder.Configure<App>()
.UsePlatformDetect()
.Start<MainWindow>();
if (args.Contains("--fbdev")) AppBuilder.Configure<App>().InitializeWithLinuxFramebuffer(tl =>
{
tl.Content = new MainView();
System.Threading.ThreadPool.QueueUserWorkItem(_ => ConsoleSilencer());
});
else
AppBuilder.Configure<App>()
.UsePlatformDetect()
.Start<MainWindow>();
}
static void ConsoleSilencer()
{
Console.CursorVisible = false;
while (true)
Console.ReadKey(true);
}
}
}

2
src/Avalonia.Controls/Button.cs

@ -226,7 +226,7 @@ namespace Avalonia.Controls
}
/// <inheritdoc/>
protected override void OnPointerReleased(PointerEventArgs e)
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
base.OnPointerReleased(e);

6
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -127,11 +127,11 @@ namespace Avalonia.Controls.Presenters
base.Render(context);
if (selectionStart == selectionEnd)
{
{
var backgroundColor = (((Control)TemplatedParent).GetValue(BackgroundProperty) as SolidColorBrush)?.Color;
var caretBrush = Brushes.Black;
if(backgroundColor.HasValue)
if (backgroundColor.HasValue)
{
byte red = (byte)~(backgroundColor.Value.R);
byte green = (byte)~(backgroundColor.Value.G);
@ -139,7 +139,7 @@ namespace Avalonia.Controls.Presenters
caretBrush = new SolidColorBrush(Color.FromRgb(red, green, blue));
}
if (_caretBlink)
{
var charPos = FormattedText.HitTestTextPosition(CaretIndex);

2
src/Avalonia.Controls/Primitives/Thumb.cs

@ -86,7 +86,7 @@ namespace Avalonia.Controls.Primitives
RaiseEvent(ev);
}
protected override void OnPointerReleased(PointerEventArgs e)
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
if (_lastPoint.HasValue)
{

2
src/Avalonia.Controls/TextBlock.cs

@ -116,7 +116,7 @@ namespace Avalonia.Controls
this.GetObservable(TextAlignmentProperty).Select(_ => Unit.Default),
this.GetObservable(FontSizeProperty).Select(_ => Unit.Default),
this.GetObservable(FontStyleProperty).Select(_ => Unit.Default),
this.GetObservable(FontWeightProperty).Select(_=>Unit.Default))
this.GetObservable(FontWeightProperty).Select(_ => Unit.Default))
.Subscribe(_ =>
{
InvalidateFormattedText();

2
src/Avalonia.Controls/TextBox.cs

@ -522,7 +522,7 @@ namespace Avalonia.Controls
}
}
protected override void OnPointerReleased(PointerEventArgs e)
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
if (_presenter != null && e.Device.Captured == _presenter)
{

4
src/Avalonia.Controls/Window.cs

@ -281,7 +281,9 @@ namespace Avalonia.Controls
var modal = PlatformImpl.ShowDialog();
var result = new TaskCompletionSource<TResult>();
Observable.FromEventPattern(this, nameof(Closed))
Observable.FromEventPattern<EventHandler, EventArgs>(
x => this.Closed += x,
x => this.Closed -= x)
.Take(1)
.Subscribe(_ =>
{

2
src/Avalonia.HtmlRenderer/HtmlControl.cs

@ -413,7 +413,7 @@ namespace Avalonia.Controls.Html
/// <summary>
/// Handle mouse up to handle selection and link click.
/// </summary>
protected override void OnPointerReleased(PointerEventArgs e)
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
base.OnPointerReleased(e);
LeftMouseButton = false;

2
src/Avalonia.Input/InputElement.cs

@ -473,7 +473,7 @@ namespace Avalonia.Input
/// Called before the <see cref="PointerReleased"/> event occurs.
/// </summary>
/// <param name="e">The event args.</param>
protected virtual void OnPointerReleased(PointerEventArgs e)
protected virtual void OnPointerReleased(PointerReleasedEventArgs e)
{
}

20
src/Avalonia.Visuals/Media/FormattedTextStyleSpan.cs

@ -2,8 +2,17 @@
namespace Avalonia.Media
{
/// <summary>
/// Describes the formatting for a span of text in a <see cref="FormattedText"/> object.
/// </summary>
public class FormattedTextStyleSpan
{
/// <summary>
/// Initializes a new instance of the <see cref="FormattedTextStyleSpan"/> class.
/// </summary>
/// <param name="startIndex">The index of the first character in the span.</param>
/// <param name="length">The length of the span.</param>
/// <param name="foregroundBrush">The span's foreground brush.</param>
public FormattedTextStyleSpan(
int startIndex,
int length,
@ -14,8 +23,19 @@ namespace Avalonia.Media
ForegroundBrush = foregroundBrush;
}
/// <summary>
/// Gets the index of the first character in the span.
/// </summary>
public int StartIndex { get; }
/// <summary>
/// Gets the length of the span.
/// </summary>
public int Length { get; }
/// <summary>
/// Gets the span's foreground brush.
/// </summary>
public IBrush ForegroundBrush { get; }
}
}

26
src/Avalonia.Visuals/Media/Typeface.cs

@ -1,10 +1,19 @@
using System;
using Avalonia.Media;
namespace Avalonia.Media
{
/// <summary>
/// Represents a typeface.
/// </summary>
public class Typeface
{
/// <summary>
/// Initializes a new instance of the <see cref="Typeface"/> class.
/// </summary>
/// <param name="fontFamilyName">The name of the font family.</param>
/// <param name="fontSize">The font size, in DIPs.</param>
/// <param name="style">The font style.</param>
/// <param name="weight">The font weight.</param>
public Typeface(
string fontFamilyName,
double fontSize,
@ -27,9 +36,24 @@ namespace Avalonia.Media
Weight = weight;
}
/// <summary>
/// Gets the name of the font family.
/// </summary>
public string FontFamilyName { get; }
/// <summary>
/// Gets the size of the font in DIPs.
/// </summary>
public double FontSize { get; }
/// <summary>
/// Gets the font style.
/// </summary>
public FontStyle Style { get; }
/// <summary>
/// Gets the font weight.
/// </summary>
public FontWeight Weight { get; }
}
}

2
src/Gtk/Avalonia.Cairo/Media/StreamGeometryImpl.cs

@ -52,7 +52,7 @@ namespace Avalonia.Cairo.Media
public Rect GetRenderBounds(double strokeThickness)
{
// TODO: Calculate properly.
return Bounds.TransformToAABB(Transform).Inflate(strokeThickness);
return Bounds.TransformToAABB(Transform).Inflate(strokeThickness);
}
public IStreamGeometryContextImpl Open()

14
src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\..\Avalonia.Controls\Avalonia.Controls.csproj" />
<ProjectReference Include="..\..\Avalonia.Input\Avalonia.Input.csproj" />
<ProjectReference Include="..\..\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\..\Skia\Avalonia.Skia.Desktop.NetStandard\Avalonia.Skia.Desktop.NetStandard.csproj" />
</ItemGroup>
</Project>

89
src/Linux/Avalonia.LinuxFramebuffer/EvDevDevice.cs

@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace Avalonia.LinuxFramebuffer
{
unsafe class EvDevDevice
{
private static readonly Lazy<List<EvDevDevice>> AllMouseDevices = new Lazy<List<EvDevDevice>>(()
=> OpenMouseDevices());
private static List<EvDevDevice> OpenMouseDevices()
{
var rv = new List<EvDevDevice>();
foreach (var dev in Directory.GetFiles("/dev/input", "event*").Select(Open))
{
if (!dev.IsMouse)
NativeUnsafeMethods.close(dev.Fd);
else
rv.Add(dev);
}
return rv;
}
public static IReadOnlyList<EvDevDevice> MouseDevices => AllMouseDevices.Value;
public int Fd { get; }
private IntPtr _dev;
public string Name { get; }
public List<EvType> EventTypes { get; private set; } = new List<EvType>();
public input_absinfo? AbsX { get; }
public input_absinfo? AbsY { get; }
public EvDevDevice(int fd, IntPtr dev)
{
Fd = fd;
_dev = dev;
Name = Marshal.PtrToStringAnsi(NativeUnsafeMethods.libevdev_get_name(_dev));
foreach (EvType type in Enum.GetValues(typeof(EvType)))
{
if (NativeUnsafeMethods.libevdev_has_event_type(dev, type) != 0)
EventTypes.Add(type);
}
var ptr = NativeUnsafeMethods.libevdev_get_abs_info(dev, (int) AbsAxis.ABS_X);
if (ptr != null)
AbsX = *ptr;
ptr = NativeUnsafeMethods.libevdev_get_abs_info(dev, (int)AbsAxis.ABS_Y);
if (ptr != null)
AbsY = *ptr;
}
public input_event? NextEvent()
{
input_event ev;
if (NativeUnsafeMethods.libevdev_next_event(_dev, 2, out ev) == 0)
return ev;
return null;
}
public bool IsMouse => EventTypes.Contains(EvType.EV_REL);
public static EvDevDevice Open(string device)
{
var fd = NativeUnsafeMethods.open(device, 2048, 0);
if (fd <= 0)
throw new Exception($"Unable to open {device} code {Marshal.GetLastWin32Error()}");
IntPtr dev;
var rc = NativeUnsafeMethods.libevdev_new_from_fd(fd, out dev);
if (rc < 0)
{
NativeUnsafeMethods.close(fd);
throw new Exception($"Unable to initialize evdev for {device} code {Marshal.GetLastWin32Error()}");
}
return new EvDevDevice(fd, dev);
}
}
public class EvDevAxisInfo
{
public int Minimum { get; set; }
public int Maximum { get; set; }
}
}

68
src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs

@ -0,0 +1,68 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Platform;
using Avalonia.Threading;
namespace Avalonia.LinuxFramebuffer
{
class FramebufferToplevelImpl : IEmbeddableWindowImpl
{
private readonly LinuxFramebuffer _fb;
private bool _renderQueued;
public IInputRoot InputRoot { get; private set; }
public FramebufferToplevelImpl(LinuxFramebuffer fb)
{
_fb = fb;
Invalidate(default(Rect));
var mice = new Mice(ClientSize.Width, ClientSize.Height);
mice.Start();
mice.Event += e => Input?.Invoke(e);
}
public void Dispose()
{
throw new NotSupportedException();
}
public void Invalidate(Rect rect)
{
if(_renderQueued)
return;
_renderQueued = true;
Dispatcher.UIThread.InvokeAsync(() =>
{
Paint?.Invoke(new Rect(default(Point), ClientSize));
_renderQueued = false;
});
}
public void SetInputRoot(IInputRoot inputRoot)
{
InputRoot = inputRoot;
}
public Point PointToClient(Point point) => point;
public Point PointToScreen(Point point) => point;
public void SetCursor(IPlatformHandle cursor)
{
}
public Size ClientSize => _fb.PixelSize;
public double Scaling => 1;
public IEnumerable<object> Surfaces => new object[] {_fb};
public Action<RawInputEventArgs> Input { get; set; }
public Action<Rect> Paint { get; set; }
public Action<Size> Resized { get; set; }
public Action<double> ScalingChanged { get; set; }
public Action Closed { get; set; }
public event Action LostFocus;
}
}

138
src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebuffer.cs

@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Platform;
namespace Avalonia.LinuxFramebuffer
{
public sealed unsafe class LinuxFramebuffer : IFramebufferPlatformSurface, IDisposable
{
private readonly Size _dpi;
private int _fd;
private fb_fix_screeninfo _fixedInfo;
private fb_var_screeninfo _varInfo;
private IntPtr _mappedLength;
private IntPtr _mappedAddress;
public LinuxFramebuffer(string fileName = null, Size? dpi = null)
{
_dpi = dpi ?? new Size(96, 96);
fileName = fileName ?? Environment.GetEnvironmentVariable("FRAMEBUFFER") ?? "/dev/fb0";
_fd = NativeUnsafeMethods.open(fileName, 2, 0);
if (_fd <= 0)
throw new Exception("Error: " + Marshal.GetLastWin32Error());
try
{
Init();
}
catch
{
Dispose();
throw;
}
}
void Init()
{
fixed (void* pnfo = &_varInfo)
{
if (-1 == NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOGET_VSCREENINFO, pnfo))
throw new Exception("FBIOGET_VSCREENINFO error: " + Marshal.GetLastWin32Error());
SetBpp();
_varInfo.yoffset = 100;
if (-1 == NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOPUT_VSCREENINFO, pnfo))
_varInfo.transp = new fb_bitfield();
if (-1 == NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOPUT_VSCREENINFO, pnfo))
throw new Exception("FBIOPUT_VSCREENINFO error: " + Marshal.GetLastWin32Error());
if (-1 == NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOGET_VSCREENINFO, pnfo))
throw new Exception("FBIOGET_VSCREENINFO error: " + Marshal.GetLastWin32Error());
if (_varInfo.bits_per_pixel != 32)
throw new Exception("Unable to set 32-bit display mode");
}
fixed(void*pnfo = &_fixedInfo)
if (-1 == NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOGET_FSCREENINFO, pnfo))
throw new Exception("FBIOGET_FSCREENINFO error: " + Marshal.GetLastWin32Error());
_mappedLength = new IntPtr(_fixedInfo.line_length * _varInfo.yres);
_mappedAddress = NativeUnsafeMethods.mmap(IntPtr.Zero, _mappedLength, 3, 1, _fd, IntPtr.Zero);
if (_mappedAddress == new IntPtr(-1))
throw new Exception($"Unable to mmap {_mappedLength} bytes, error {Marshal.GetLastWin32Error()}");
fixed (fb_fix_screeninfo* pnfo = &_fixedInfo)
{
int idlen;
for (idlen = 0; idlen < 16 && pnfo->id[idlen] != 0; idlen++) ;
Id = Encoding.ASCII.GetString(pnfo->id, idlen);
}
}
void SetBpp()
{
_varInfo.bits_per_pixel = 32;
_varInfo.grayscale = 0;
_varInfo.red = _varInfo.blue = _varInfo.green = _varInfo.transp = new fb_bitfield
{
length = 8
};
_varInfo.green.offset = 8;
_varInfo.blue.offset = 16;
_varInfo.transp.offset = 24;
}
public string Id { get; private set; }
public Size PixelSize
{
get
{
fb_var_screeninfo nfo;
if (-1 == NativeUnsafeMethods.ioctl(_fd, FbIoCtl.FBIOGET_VSCREENINFO, &nfo))
throw new Exception("FBIOGET_VSCREENINFO error: " + Marshal.GetLastWin32Error());
return new Size(nfo.xres, nfo.yres);
}
}
public ILockedFramebuffer Lock()
{
if (_fd <= 0)
throw new ObjectDisposedException("LinuxFramebuffer");
return new LockedFramebuffer(_fd, _fixedInfo, _varInfo, _mappedAddress, _dpi);
}
private void ReleaseUnmanagedResources()
{
if (_mappedAddress != IntPtr.Zero)
{
NativeUnsafeMethods.munmap(_mappedAddress, _mappedLength);
_mappedAddress = IntPtr.Zero;
}
if(_fd == 0)
return;
NativeUnsafeMethods.close(_fd);
_fd = 0;
}
public void Dispose()
{
ReleaseUnmanagedResources();
GC.SuppressFinalize(this);
}
~LinuxFramebuffer()
{
ReleaseUnmanagedResources();
}
}
}

75
src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using Avalonia.Controls;
using Avalonia.Controls.Embedding;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.LinuxFramebuffer;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Threading;
namespace Avalonia.LinuxFramebuffer
{
class LinuxFramebufferPlatform
{
LinuxFramebuffer _fb;
public static KeyboardDevice KeyboardDevice = new KeyboardDevice();
public static MouseDevice MouseDevice = new MouseDevice();
private static readonly Stopwatch St = Stopwatch.StartNew();
internal static uint Timestamp => (uint)St.ElapsedTicks;
public static FramebufferToplevelImpl TopLevel;
LinuxFramebufferPlatform(string fbdev = null)
{
_fb = new LinuxFramebuffer(fbdev);
}
void Initialize()
{
AvaloniaLocator.CurrentMutable
.Bind<IStandardCursorFactory>().ToTransient<CursorFactoryStub>()
.Bind<IKeyboardDevice>().ToConstant(KeyboardDevice)
.Bind<IMouseDevice>().ToConstant(MouseDevice)
.Bind<IPlatformSettings>().ToSingleton<PlatformSettings>()
.Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance)
.Bind<IRenderLoop>().ToConstant(PlatformThreadingInterface.Instance);
}
internal static TopLevel Initialize<T>(T builder, string fbdev = null) where T : AppBuilderBase<T>, new()
{
var platform = new LinuxFramebufferPlatform(fbdev);
builder.UseSkia().UseWindowingSubsystem(platform.Initialize, "fbdev")
.SetupWithoutStarting();
var tl = new EmbeddableControlRoot(TopLevel = new FramebufferToplevelImpl(platform._fb));
tl.Prepare();
return tl;
}
}
}
public static class LinuxFramebufferPlatformExtensions
{
class TokenClosable : ICloseable
{
public event EventHandler Closed;
public TokenClosable(CancellationToken token)
{
token.Register(() => Dispatcher.UIThread.InvokeAsync(() => Closed?.Invoke(this, new EventArgs())));
}
}
public static void InitializeWithLinuxFramebuffer<T>(this T builder, Action<TopLevel> setup,
CancellationToken stop = default(CancellationToken), string fbdev = null)
where T : AppBuilderBase<T>, new()
{
setup(LinuxFramebufferPlatform.Initialize(builder, fbdev));
builder.BeforeStartCallback(builder);
builder.Instance.Run(new TokenClosable(stop));
}
}

47
src/Linux/Avalonia.LinuxFramebuffer/LockedFramebuffer.cs

@ -0,0 +1,47 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.Platform;
namespace Avalonia.LinuxFramebuffer
{
unsafe class LockedFramebuffer : ILockedFramebuffer
{
private readonly int _fb;
private readonly fb_fix_screeninfo _fixedInfo;
private fb_var_screeninfo _varInfo;
private readonly IntPtr _address;
public LockedFramebuffer(int fb, fb_fix_screeninfo fixedInfo, fb_var_screeninfo varInfo, IntPtr address, Size dpi)
{
_fb = fb;
_fixedInfo = fixedInfo;
_varInfo = varInfo;
_address = address;
Dpi = dpi;
//Use double buffering to avoid flicker
Address = Marshal.AllocHGlobal(RowBytes * Height);
}
void VSync()
{
NativeUnsafeMethods.ioctl(_fb, FbIoCtl.FBIO_WAITFORVSYNC, null);
}
public void Dispose()
{
VSync();
NativeUnsafeMethods.memcpy(_address, Address, new IntPtr(RowBytes * Height));
Marshal.FreeHGlobal(Address);
Address = IntPtr.Zero;
}
public IntPtr Address { get; private set; }
public int Width => (int)_varInfo.xres;
public int Height => (int) _varInfo.yres;
public int RowBytes => (int) _fixedInfo.line_length;
public Size Dpi { get; }
public PixelFormat Format => _varInfo.blue.offset == 16 ? PixelFormat.Rgba8888 : PixelFormat.Bgra8888;
}
}

120
src/Linux/Avalonia.LinuxFramebuffer/Mice.cs

@ -0,0 +1,120 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Platform;
namespace Avalonia.LinuxFramebuffer
{
public unsafe class Mice
{
private readonly double _width;
private readonly double _height;
private double _x;
private double _y;
public event Action<RawInputEventArgs> Event;
public Mice(double width, double height)
{
_width = width;
_height = height;
}
public void Start() => AvaloniaLocator.Current.GetService<IRuntimePlatform>().PostThreadPoolItem(Worker);
private void Worker()
{
var mouseDevices = EvDevDevice.MouseDevices.Where(d => d.IsMouse).ToList();
if (mouseDevices.Count == 0)
return;
var are = new AutoResetEvent(false);
while (true)
{
try
{
var rfds = new fd_set {count = mouseDevices.Count};
for (int c = 0; c < mouseDevices.Count; c++)
rfds.fds[c] = mouseDevices[c].Fd;
IntPtr* timeval = stackalloc IntPtr[2];
timeval[0] = new IntPtr(0);
timeval[1] = new IntPtr(100);
are.WaitOne(30);
foreach (var dev in mouseDevices)
{
while(true)
{
var ev = dev.NextEvent();
if (!ev.HasValue)
break;
PlatformThreadingInterface.Instance.Send(() => ProcessEvent(dev, ev.Value));
}
}
}
catch (Exception e)
{
Console.Error.WriteLine(e.ToString());
}
}
}
static double TranslateAxis(input_absinfo axis, int value, double max)
{
return (value - axis.minimum) / (double) (axis.maximum - axis.minimum) * max;
}
private void ProcessEvent(EvDevDevice device, input_event ev)
{
if (ev.type == (short)EvType.EV_REL)
{
if (ev.code == (short) AxisEventCode.REL_X)
_x = Math.Min(_width, Math.Max(0, _x + ev.value));
else if (ev.code == (short) AxisEventCode.REL_Y)
_y = Math.Min(_height, Math.Max(0, _y + ev.value));
else
return;
Event?.Invoke(new RawMouseEventArgs(LinuxFramebufferPlatform.MouseDevice,
LinuxFramebufferPlatform.Timestamp,
LinuxFramebufferPlatform.TopLevel.InputRoot, RawMouseEventType.Move, new Point(_x, _y),
InputModifiers.None));
}
if (ev.type ==(int) EvType.EV_ABS)
{
if (ev.code == (short) AbsAxis.ABS_X && device.AbsX.HasValue)
_x = TranslateAxis(device.AbsX.Value, ev.value, _width);
else if (ev.code == (short) AbsAxis.ABS_Y && device.AbsY.HasValue)
_y = TranslateAxis(device.AbsY.Value, ev.value, _height);
else
return;
Event?.Invoke(new RawMouseEventArgs(LinuxFramebufferPlatform.MouseDevice,
LinuxFramebufferPlatform.Timestamp,
LinuxFramebufferPlatform.TopLevel.InputRoot, RawMouseEventType.Move, new Point(_x, _y),
InputModifiers.None));
}
if (ev.type == (short) EvType.EV_KEY)
{
RawMouseEventType? type = null;
if (ev.code == (ushort) EvKey.BTN_LEFT)
type = ev.value == 1 ? RawMouseEventType.LeftButtonDown : RawMouseEventType.LeftButtonUp;
if (ev.code == (ushort)EvKey.BTN_RIGHT)
type = ev.value == 1 ? RawMouseEventType.RightButtonDown : RawMouseEventType.RightButtonUp;
if (ev.code == (ushort) EvKey.BTN_MIDDLE)
type = ev.value == 1 ? RawMouseEventType.MiddleButtonDown : RawMouseEventType.MiddleButtonUp;
if (!type.HasValue)
return;
Event?.Invoke(new RawMouseEventArgs(LinuxFramebufferPlatform.MouseDevice,
LinuxFramebufferPlatform.Timestamp,
LinuxFramebufferPlatform.TopLevel.InputRoot, type.Value, new Point(_x, _y), default(InputModifiers)));
}
}
}
}

254
src/Linux/Avalonia.LinuxFramebuffer/NativeUnsafeMethods.cs

@ -0,0 +1,254 @@
using __u32 = System.UInt32;
using __s32 = System.Int32;
using __u16 = System.UInt16;
using System;
using System.Runtime.InteropServices;
// ReSharper disable FieldCanBeMadeReadOnly.Local
// ReSharper disable ArrangeTypeMemberModifiers
// ReSharper disable BuiltInTypeReferenceStyle
// ReSharper disable InconsistentNaming
namespace Avalonia.LinuxFramebuffer
{
unsafe class NativeUnsafeMethods
{
[DllImport("libc", EntryPoint = "open", SetLastError = true)]
public static extern int open(string pathname, int flags, int mode);
[DllImport("libc", EntryPoint = "close", SetLastError = true)]
public static extern int close(int fd);
[DllImport("libc", EntryPoint = "ioctl", SetLastError = true)]
public static extern int ioctl(int fd, FbIoCtl code, void* arg);
[DllImport("libc", EntryPoint = "mmap", SetLastError = true)]
public static extern IntPtr mmap(IntPtr addr, IntPtr length, int prot, int flags,
int fd, IntPtr offset);
[DllImport("libc", EntryPoint = "munmap", SetLastError = true)]
public static extern int munmap(IntPtr addr, IntPtr length);
[DllImport("libc", EntryPoint = "memcpy", SetLastError = true)]
public static extern int memcpy(IntPtr dest, IntPtr src, IntPtr length);
[DllImport("libc", EntryPoint = "select", SetLastError = true)]
public static extern int select(int nfds, void* rfds, void* wfds, void* exfds, IntPtr* timevals);
[DllImport("libevdev.so.2", EntryPoint = "libevdev_new_from_fd", SetLastError = true)]
public static extern int libevdev_new_from_fd(int fd, out IntPtr dev);
[DllImport("libevdev.so.2", EntryPoint = "libevdev_has_event_type", SetLastError = true)]
public static extern int libevdev_has_event_type(IntPtr dev, EvType type);
[DllImport("libevdev.so.2", EntryPoint = "libevdev_next_event", SetLastError = true)]
public static extern int libevdev_next_event(IntPtr dev, int flags, out input_event ev);
[DllImport("libevdev.so.2", EntryPoint = "libevdev_get_name", SetLastError = true)]
public static extern IntPtr libevdev_get_name(IntPtr dev);
[DllImport("libevdev.so.2", EntryPoint = "libevdev_get_abs_info", SetLastError = true)]
public static extern input_absinfo* libevdev_get_abs_info(IntPtr dev, int code);
}
enum FbIoCtl : uint
{
FBIOGET_VSCREENINFO = 0x4600,
FBIOPUT_VSCREENINFO = 0x4601,
FBIOGET_FSCREENINFO = 0x4602,
FBIOGET_VBLANK = 0x80204612u,
FBIO_WAITFORVSYNC = 0x40044620,
FBIOPAN_DISPLAY = 0x4606
}
[Flags]
enum VBlankFlags
{
FB_VBLANK_VBLANKING = 0x001 /* currently in a vertical blank */,
FB_VBLANK_HBLANKING = 0x002 /* currently in a horizontal blank */,
FB_VBLANK_HAVE_VBLANK = 0x004 /* vertical blanks can be detected */,
FB_VBLANK_HAVE_HBLANK = 0x008 /* horizontal blanks can be detected */,
FB_VBLANK_HAVE_COUNT = 0x010 /* global retrace counter is available */,
FB_VBLANK_HAVE_VCOUNT = 0x020 /* the vcount field is valid */,
FB_VBLANK_HAVE_HCOUNT = 0x040 /* the hcount field is valid */,
FB_VBLANK_VSYNCING = 0x080 /* currently in a vsync */,
FB_VBLANK_HAVE_VSYNC = 0x100 /* verical syncs can be detected */
}
unsafe struct fb_vblank {
public VBlankFlags flags; /* FB_VBLANK flags */
__u32 count; /* counter of retraces since boot */
__u32 vcount; /* current scanline position */
__u32 hcount; /* current scandot position */
fixed __u32 reserved[4]; /* reserved for future compatibility */
};
[StructLayout(LayoutKind.Sequential)]
unsafe struct fb_fix_screeninfo
{
public fixed byte id[16]; /* identification string eg "TT Builtin" */
public IntPtr smem_start; /* Start of frame buffer mem */
/* (physical address) */
public __u32 smem_len; /* Length of frame buffer mem */
public __u32 type; /* see FB_TYPE_* */
public __u32 type_aux; /* Interleave for interleaved Planes */
public __u32 visual; /* see FB_VISUAL_* */
public __u16 xpanstep; /* zero if no hardware panning */
public __u16 ypanstep; /* zero if no hardware panning */
public __u16 ywrapstep; /* zero if no hardware ywrap */
public __u32 line_length; /* length of a line in bytes */
public IntPtr mmio_start; /* Start of Memory Mapped I/O */
/* (physical address) */
public __u32 mmio_len; /* Length of Memory Mapped I/O */
public __u32 accel; /* Type of acceleration available */
public fixed __u16 reserved[3]; /* Reserved for future compatibility */
};
[StructLayout(LayoutKind.Sequential)]
struct fb_bitfield
{
public __u32 offset; /* beginning of bitfield */
public __u32 length; /* length of bitfield */
public __u32 msb_right; /* != 0 : Most significant bit is */
/* right */
};
[StructLayout(LayoutKind.Sequential)]
unsafe struct fb_var_screeninfo
{
public __u32 xres; /* visible resolution */
public __u32 yres;
public __u32 xres_virtual; /* virtual resolution */
public __u32 yres_virtual;
public __u32 xoffset; /* offset from virtual to visible */
public __u32 yoffset; /* resolution */
public __u32 bits_per_pixel; /* guess what */
public __u32 grayscale; /* != 0 Graylevels instead of colors */
public fb_bitfield red; /* bitfield in fb mem if true color, */
public fb_bitfield green; /* else only length is significant */
public fb_bitfield blue;
public fb_bitfield transp; /* transparency */
public __u32 nonstd; /* != 0 Non standard pixel format */
public __u32 activate; /* see FB_ACTIVATE_* */
public __u32 height; /* height of picture in mm */
public __u32 width; /* width of picture in mm */
public __u32 accel_flags; /* acceleration flags (hints) */
/* Timing: All values in pixclocks, except pixclock (of course) */
public __u32 pixclock; /* pixel clock in ps (pico seconds) */
public __u32 left_margin; /* time from sync to picture */
public __u32 right_margin; /* time from picture to sync */
public __u32 upper_margin; /* time from sync to picture */
public __u32 lower_margin;
public __u32 hsync_len; /* length of horizontal sync */
public __u32 vsync_len; /* length of vertical sync */
public __u32 sync; /* see FB_SYNC_* */
public __u32 vmode; /* see FB_VMODE_* */
public fixed __u32 reserved[6]; /* Reserved for future compatibility */
};
enum EvType
{
EV_SYN = 0x00,
EV_KEY = 0x01,
EV_REL = 0x02,
EV_ABS = 0x03,
EV_MSC = 0x04,
EV_SW = 0x05,
EV_LED = 0x11,
EV_SND = 0x12,
EV_REP = 0x14,
EV_FF = 0x15,
EV_PWR = 0x16,
EV_FF_STATUS = 0x17,
}
[StructLayout(LayoutKind.Sequential)]
struct input_event
{
private IntPtr crap1, crap2;
public ushort type, code;
public int value;
}
[StructLayout(LayoutKind.Sequential)]
unsafe struct fd_set
{
public int count;
public fixed int fds [256];
}
enum AxisEventCode
{
REL_X = 0x00,
REL_Y = 0x01,
REL_Z = 0x02,
REL_RX = 0x03,
REL_RY = 0x04,
REL_RZ = 0x05,
REL_HWHEEL = 0x06,
REL_DIAL = 0x07,
REL_WHEEL = 0x08,
REL_MISC = 0x09,
REL_MAX = 0x0f
}
enum AbsAxis
{
ABS_X = 0x00,
ABS_Y = 0x01,
ABS_Z = 0x02,
ABS_RX = 0x03,
ABS_RY = 0x04,
ABS_RZ = 0x05,
ABS_THROTTLE = 0x06,
ABS_RUDDER = 0x07,
ABS_WHEEL = 0x08,
ABS_GAS = 0x09,
ABS_BRAKE = 0x0a,
ABS_HAT0X = 0x10,
ABS_HAT0Y = 0x11,
ABS_HAT1X = 0x12,
ABS_HAT1Y = 0x13,
ABS_HAT2X = 0x14,
ABS_HAT2Y = 0x15,
ABS_HAT3X = 0x16,
ABS_HAT3Y = 0x17,
ABS_PRESSURE = 0x18,
ABS_DISTANCE = 0x19,
ABS_TILT_X = 0x1a,
ABS_TILT_Y = 0x1b,
ABS_TOOL_WIDTH = 0x1c
}
enum EvKey
{
BTN_LEFT = 0x110,
BTN_RIGHT = 0x111,
BTN_MIDDLE = 0x112
}
[StructLayout(LayoutKind.Sequential)]
struct input_absinfo
{
public __s32 value;
public __s32 minimum;
public __s32 maximum;
public __s32 fuzz;
public __s32 flat;
public __s32 resolution;
}
}

112
src/Linux/Avalonia.LinuxFramebuffer/PlatformThreadingInterface.cs

@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Platform;
using Avalonia.Rendering;
namespace Avalonia.LinuxFramebuffer
{
class PlatformThreadingInterface : IPlatformThreadingInterface, IRenderLoop
{
public static PlatformThreadingInterface Instance { get; } = new PlatformThreadingInterface();
public PlatformThreadingInterface()
{
TlsCurrentThreadIsLoopThread = true;
StartTimer(new TimeSpan(0, 0, 0, 0, 66), () => Tick?.Invoke(this, new EventArgs()));
}
private readonly AutoResetEvent _signaled = new AutoResetEvent(false);
private readonly AutoResetEvent _queued = new AutoResetEvent(false);
private readonly Queue<Action> _actions = new Queue<Action>();
public void RunLoop(CancellationToken cancellationToken)
{
var handles = new[] {_signaled, _queued};
while (true)
{
if (0 == WaitHandle.WaitAny(handles))
Signaled?.Invoke();
else
{
while (true)
{
Action item;
lock(_actions)
if (_actions.Count == 0)
break;
else
item = _actions.Dequeue();
item();
}
}
}
}
public void Send(Action cb)
{
lock (_actions)
{
_actions.Enqueue(cb);
_queued.Set();
}
}
class WatTimer : IDisposable
{
private readonly IDisposable _timer;
private GCHandle _handle;
public WatTimer(IDisposable timer)
{
_timer = timer;
_handle = GCHandle.Alloc(_timer);
}
public void Dispose()
{
_handle.Free();
_timer.Dispose();
}
}
public IDisposable StartTimer(TimeSpan interval, Action tick)
{
return new WatTimer(new System.Threading.Timer(delegate
{
var tcs = new TaskCompletionSource<int>();
Send(() =>
{
try
{
tick();
}
finally
{
tcs.SetResult(0);
}
});
tcs.Task.Wait();
}, null, TimeSpan.Zero, interval));
}
public void Signal()
{
_signaled.Set();
}
[ThreadStatic] private static bool TlsCurrentThreadIsLoopThread;
public bool CurrentThreadIsLoopThread => TlsCurrentThreadIsLoopThread;
public event Action Signaled;
public event EventHandler<EventArgs> Tick;
}
}

21
src/Linux/Avalonia.LinuxFramebuffer/Stubs.cs

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Text;
using Avalonia.Input;
using Avalonia.Platform;
namespace Avalonia.LinuxFramebuffer
{
internal class CursorFactoryStub : IStandardCursorFactory
{
public IPlatformHandle GetCursor(StandardCursorType cursorType)
{
return new PlatformHandle(IntPtr.Zero, null);
}
}
internal class PlatformSettings : IPlatformSettings
{
public Size DoubleClickSize { get; } = new Size(4, 4);
public TimeSpan DoubleClickTime { get; } = new TimeSpan(0, 0, 0, 0, 500);
}
}

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

@ -39,19 +39,6 @@ namespace Avalonia.Direct2D1
private static readonly SharpDX.WIC.ImagingFactory s_imagingFactory = new SharpDX.WIC.ImagingFactory();
public static void Initialize()
{
AvaloniaLocator.CurrentMutable
.Bind<IPlatformRenderInterface>().ToConstant(s_instance)
.Bind<SharpDX.Direct2D1.Factory>().ToConstant(s_d2D1Factory)
.Bind<SharpDX.Direct2D1.Factory1>().ToConstant(s_d2D1Factory)
.BindToSelf(s_dwfactory)
.BindToSelf(s_imagingFactory)
.BindToSelf(s_dxgiDevice)
.BindToSelf(s_d2D1Device);
SharpDX.Configuration.EnableReleaseOnFinalizer = true;
}
private static readonly SharpDX.DXGI.Device s_dxgiDevice;
private static readonly SharpDX.Direct2D1.Device s_d2D1Device;
@ -83,6 +70,19 @@ namespace Avalonia.Direct2D1
}
}
public static void Initialize()
{
AvaloniaLocator.CurrentMutable
.Bind<IPlatformRenderInterface>().ToConstant(s_instance)
.Bind<SharpDX.Direct2D1.Factory>().ToConstant(s_d2D1Factory)
.Bind<SharpDX.Direct2D1.Factory1>().ToConstant(s_d2D1Factory)
.BindToSelf(s_dwfactory)
.BindToSelf(s_imagingFactory)
.BindToSelf(s_dxgiDevice)
.BindToSelf(s_d2D1Device);
SharpDX.Configuration.EnableReleaseOnFinalizer = true;
}
public IBitmapImpl CreateBitmap(int width, int height)
{
return new WicBitmapImpl(s_imagingFactory, width, height);

4
tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs

@ -163,7 +163,7 @@ namespace Avalonia.Layout.UnitTests
{
var globalStyles = new Mock<IGlobalStyles>();
var renderInterface = new Mock<IPlatformRenderInterface>();
renderInterface.Setup(x =>
renderInterface.Setup(x =>
x.CreateFormattedText(
It.IsAny<string>(),
It.IsAny<Typeface>(),
@ -172,7 +172,7 @@ namespace Avalonia.Layout.UnitTests
It.IsAny<Size>(),
It.IsAny<IReadOnlyList<FormattedTextStyleSpan>>()))
.Returns(new FormattedTextMock("TEST"));
var windowImpl = new Mock<IWindowImpl>();
Size clientSize = default(Size);

15
tests/Avalonia.UnitTests/TestServices.cs

@ -13,6 +13,7 @@ using Avalonia.Styling;
using Avalonia.Themes.Default;
using Avalonia.Rendering;
using System.Reactive.Concurrency;
using System.Collections.Generic;
namespace Avalonia.UnitTests
{
@ -165,5 +166,19 @@ namespace Avalonia.UnitTests
return result;
}
private static IPlatformRenderInterface CreateRenderInterfaceMock()
{
return Mock.Of<IPlatformRenderInterface>(x =>
x.CreateFormattedText(
It.IsAny<string>(),
It.IsAny<Typeface>(),
It.IsAny<TextAlignment>(),
It.IsAny<TextWrapping>(),
It.IsAny<Size>(),
It.IsAny<IReadOnlyList<FormattedTextStyleSpan>>()) == Mock.Of<IFormattedTextImpl>() &&
x.CreateStreamGeometry() == Mock.Of<IStreamGeometryImpl>(
y => y.Open() == Mock.Of<IStreamGeometryContextImpl>()));
}
}
}

4
tests/Avalonia.Visuals.UnitTests/Media/TypefaceTests.cs

@ -7,13 +7,13 @@ namespace Avalonia.Visuals.UnitTests.Media
public class TypefaceTests
{
[Fact]
public void Exception_Should_Be_Thrown_If_FontSize_0()
public void Exception_Should_Be_Thrown_If_FontSize_LessThanEqualTo_0()
{
Assert.Throws<ArgumentException>(() => new Typeface("foo", 0));
}
[Fact]
public void Exception_Should_Be_Thrown_If_FontWeight_0()
public void Exception_Should_Be_Thrown_If_FontWeight_LessThanEqualTo_0()
{
Assert.Throws<ArgumentException>(() => new Typeface("foo", 12, weight: 0));
}

Loading…
Cancel
Save