diff --git a/Avalonia.sln b/Avalonia.sln
index 3fb5ec2693..cc166bc495 100644
--- a/Avalonia.sln
+++ b/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
diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
index d43c3a060e..e0e848b91b 100644
--- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
+++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
@@ -7,6 +7,7 @@
+
diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs
index 57a508f923..f1146de013 100644
--- a/samples/ControlCatalog.NetCore/Program.cs
+++ b/samples/ControlCatalog.NetCore/Program.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using Avalonia;
namespace ControlCatalog.NetCore
@@ -7,9 +8,13 @@ namespace ControlCatalog.NetCore
{
static void Main(string[] args)
{
- AppBuilder.Configure()
- .UsePlatformDetect()
- .Start();
+ if (args.Contains("--fbdev"))
+ AppBuilder.Configure()
+ .InitializeWithLinuxFramebuffer(tl => tl.Content = new MainView());
+ else
+ AppBuilder.Configure()
+ .UsePlatformDetect()
+ .Start();
}
}
}
\ No newline at end of file
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj b/src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj
new file mode 100644
index 0000000000..3a3e08135d
--- /dev/null
+++ b/src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj
@@ -0,0 +1,14 @@
+
+
+ netstandard1.3
+ true
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/EvDevDevice.cs b/src/Linux/Avalonia.LinuxFramebuffer/EvDevDevice.cs
new file mode 100644
index 0000000000..7b399863f7
--- /dev/null
+++ b/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> AllMouseDevices = new Lazy>(()
+ => OpenMouseDevices());
+
+ private static List OpenMouseDevices()
+ {
+ var rv = new List();
+ 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 MouseDevices => AllMouseDevices.Value;
+
+
+ public int Fd { get; }
+ private IntPtr _dev;
+ public string Name { get; }
+ public List EventTypes { get; private set; } = new List();
+ 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; }
+ }
+}
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
new file mode 100644
index 0000000000..193d2c1d05
--- /dev/null
+++ b/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