diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props
index cf8e0fd13a..c03ad0fefd 100644
--- a/build/SkiaSharp.props
+++ b/build/SkiaSharp.props
@@ -1,6 +1,6 @@
-
+
diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs
index 40321496c0..de9ca02ed1 100644
--- a/samples/ControlCatalog.NetCore/Program.cs
+++ b/samples/ControlCatalog.NetCore/Program.cs
@@ -4,6 +4,7 @@ using System.Linq;
using System.Threading;
using Avalonia;
using Avalonia.Controls;
+using Avalonia.LinuxFramebuffer.Output;
using Avalonia.Skia;
using Avalonia.ReactiveUI;
@@ -29,8 +30,13 @@ namespace ControlCatalog.NetCore
var builder = BuildAvaloniaApp();
if (args.Contains("--fbdev"))
{
- System.Threading.ThreadPool.QueueUserWorkItem(_ => ConsoleSilencer());
- return builder.StartLinuxFramebuffer(args);
+ SilenceConsole();
+ return builder.StartLinuxFbDev(args);
+ }
+ else if (args.Contains("--drm"))
+ {
+ SilenceConsole();
+ return builder.StartLinuxDrm(args);
}
else
return builder.StartWithClassicDesktopLifetime(args);
@@ -51,11 +57,14 @@ namespace ControlCatalog.NetCore
.UseSkia()
.UseReactiveUI();
- static void ConsoleSilencer()
+ static void SilenceConsole()
{
- Console.CursorVisible = false;
- while (true)
- Console.ReadKey(true);
+ new Thread(() =>
+ {
+ Console.CursorVisible = false;
+ while (true)
+ Console.ReadKey(true);
+ }) {IsBackground = true}.Start();
}
}
}
diff --git a/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs b/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs
index bb357453ff..cb1291410a 100644
--- a/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs
+++ b/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs
@@ -9,94 +9,69 @@ using Avalonia.Threading;
namespace Avalonia.Controls.Platform
{
- public class InternalPlatformThreadingInterface : IPlatformThreadingInterface, IRenderTimer
+ public class InternalPlatformThreadingInterface : IPlatformThreadingInterface
{
public InternalPlatformThreadingInterface()
{
TlsCurrentThreadIsLoopThread = true;
- StartTimer(
- DispatcherPriority.Render,
- new TimeSpan(0, 0, 0, 0, 66),
- () => Tick?.Invoke(TimeSpan.FromMilliseconds(Environment.TickCount)));
}
private readonly AutoResetEvent _signaled = new AutoResetEvent(false);
- private readonly AutoResetEvent _queued = new AutoResetEvent(false);
- private readonly Queue _actions = new Queue();
public void RunLoop(CancellationToken cancellationToken)
{
- var handles = new[] {_signaled, _queued};
while (true)
{
- if (0 == WaitHandle.WaitAny(handles))
- Signaled?.Invoke(null);
- else
- {
- while (true)
- {
- Action item;
- lock (_actions)
- if (_actions.Count == 0)
- break;
- else
- item = _actions.Dequeue();
- item();
- }
- }
+ Signaled?.Invoke(null);
+ _signaled.WaitOne();
}
}
- public void Send(Action cb)
- {
- lock (_actions)
- {
- _actions.Enqueue(cb);
- _queued.Set();
- }
- }
- class WatTimer : IDisposable
+ class TimerImpl : IDisposable
{
- private readonly IDisposable _timer;
+ private readonly DispatcherPriority _priority;
+ private readonly TimeSpan _interval;
+ private readonly Action _tick;
+ private Timer _timer;
private GCHandle _handle;
- public WatTimer(IDisposable timer)
+ public TimerImpl(DispatcherPriority priority, TimeSpan interval, Action tick)
{
- _timer = timer;
+ _priority = priority;
+ _interval = interval;
+ _tick = tick;
+ _timer = new Timer(OnTimer, null, interval, TimeSpan.FromMilliseconds(-1));
_handle = GCHandle.Alloc(_timer);
}
+ private void OnTimer(object state)
+ {
+ if (_timer == null)
+ return;
+ Dispatcher.UIThread.Post(() =>
+ {
+
+ if (_timer == null)
+ return;
+ _tick();
+ _timer?.Change(_interval, TimeSpan.FromMilliseconds(-1));
+ });
+ }
+
+
public void Dispose()
{
_handle.Free();
_timer.Dispose();
+ _timer = null;
}
}
public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick)
{
- return new WatTimer(new System.Threading.Timer(delegate
- {
- var tcs = new TaskCompletionSource();
- Send(() =>
- {
- try
- {
- tick();
- }
- finally
- {
- tcs.SetResult(0);
- }
- });
-
-
- tcs.Task.Wait();
- }, null, TimeSpan.Zero, interval));
-
-
+ return new TimerImpl(priority, interval, tick);
}
public void Signal(DispatcherPriority prio)
diff --git a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs
index 3b6d071583..a7a94130ea 100644
--- a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs
+++ b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs
@@ -54,7 +54,7 @@ namespace Avalonia.DesignerSupport.Remote
.Bind().ToConstant(instance)
.Bind().ToConstant(threading)
.Bind().ToConstant(new RenderLoop())
- .Bind().ToConstant(threading)
+ .Bind().ToConstant(new DefaultRenderTimer(60))
.Bind().ToSingleton()
.Bind().ToConstant(instance)
.Bind().ToSingleton()
diff --git a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs b/src/Avalonia.Input/Raw/RawPointerEventArgs.cs
index b728844e97..6fac90f255 100644
--- a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs
+++ b/src/Avalonia.Input/Raw/RawPointerEventArgs.cs
@@ -19,7 +19,8 @@ namespace Avalonia.Input.Raw
NonClientLeftButtonDown,
TouchBegin,
TouchUpdate,
- TouchEnd
+ TouchEnd,
+ TouchCancel
}
///
diff --git a/src/Avalonia.Input/TouchDevice.cs b/src/Avalonia.Input/TouchDevice.cs
index 7f473bb320..c85f98b04a 100644
--- a/src/Avalonia.Input/TouchDevice.cs
+++ b/src/Avalonia.Input/TouchDevice.cs
@@ -61,6 +61,12 @@ namespace Avalonia.Input
pointer.IsPrimary ? MouseButton.Left : MouseButton.None));
}
}
+ if (args.Type == RawPointerEventType.TouchCancel)
+ {
+ _pointers.Remove(args.TouchPointId);
+ using (pointer)
+ pointer.Capture(null);
+ }
if (args.Type == RawPointerEventType.TouchUpdate)
{
@@ -68,6 +74,8 @@ namespace Avalonia.Input
target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, target, pointer, args.Root,
args.Position, ev.Timestamp, new PointerPointProperties(modifiers), modifiers));
}
+
+
}
}
diff --git a/src/Avalonia.OpenGL/EglContext.cs b/src/Avalonia.OpenGL/EglContext.cs
index 17caf84179..a39000f198 100644
--- a/src/Avalonia.OpenGL/EglContext.cs
+++ b/src/Avalonia.OpenGL/EglContext.cs
@@ -10,7 +10,7 @@ namespace Avalonia.OpenGL
private readonly EglInterface _egl;
private readonly object _lock = new object();
- public EglContext(EglDisplay display, EglInterface egl, IntPtr ctx, IntPtr offscreenSurface)
+ public EglContext(EglDisplay display, EglInterface egl, IntPtr ctx, EglSurface offscreenSurface)
{
_disp = display;
_egl = egl;
@@ -19,7 +19,7 @@ namespace Avalonia.OpenGL
}
public IntPtr Context { get; }
- public IntPtr OffscreenSurface { get; }
+ public EglSurface OffscreenSurface { get; }
public IGlDisplay Display => _disp;
public IDisposable Lock()
@@ -36,8 +36,8 @@ namespace Avalonia.OpenGL
public void MakeCurrent(EglSurface surface)
{
- var surf = surface?.DangerousGetHandle() ?? OffscreenSurface;
- if (!_egl.MakeCurrent(_disp.Handle, surf, surf, Context))
+ var surf = surface ?? OffscreenSurface;
+ if (!_egl.MakeCurrent(_disp.Handle, surf.DangerousGetHandle(), surf.DangerousGetHandle(), Context))
throw OpenGlException.GetFormattedException("eglMakeCurrent", _egl);
}
}
diff --git a/src/Avalonia.OpenGL/EglDisplay.cs b/src/Avalonia.OpenGL/EglDisplay.cs
index b2b5a1a646..66418c0e15 100644
--- a/src/Avalonia.OpenGL/EglDisplay.cs
+++ b/src/Avalonia.OpenGL/EglDisplay.cs
@@ -12,49 +12,62 @@ namespace Avalonia.OpenGL
private readonly IntPtr _display;
private readonly IntPtr _config;
private readonly int[] _contextAttributes;
+ private readonly int _surfaceType;
public IntPtr Handle => _display;
private AngleOptions.PlatformApi? _angleApi;
- public EglDisplay(EglInterface egl)
+
+ public EglDisplay(EglInterface egl) : this(egl, -1, IntPtr.Zero, null)
+ {
+
+ }
+ public EglDisplay(EglInterface egl, int platformType, IntPtr platformDisplay, int[] attrs)
{
- _egl = egl;
+ _egl = egl;
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ if (platformType == -1 && platformDisplay == IntPtr.Zero)
{
- if (_egl.GetPlatformDisplayEXT == null)
- throw new OpenGlException("eglGetPlatformDisplayEXT is not supported by libegl.dll");
-
- var allowedApis = AvaloniaLocator.Current.GetService()?.AllowedPlatformApis
- ?? new List {AngleOptions.PlatformApi.DirectX9};
-
- foreach (var platformApi in allowedApis)
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- int dapi;
- if (platformApi == AngleOptions.PlatformApi.DirectX9)
- dapi = EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE;
- else if (platformApi == AngleOptions.PlatformApi.DirectX11)
- dapi = EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE;
- else
- continue;
-
- _display = _egl.GetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, IntPtr.Zero, new[]
- {
- EGL_PLATFORM_ANGLE_TYPE_ANGLE, dapi, EGL_NONE
- });
- if (_display != IntPtr.Zero)
+ if (_egl.GetPlatformDisplayEXT == null)
+ throw new OpenGlException("eglGetPlatformDisplayEXT is not supported by libegl.dll");
+
+ var allowedApis = AvaloniaLocator.Current.GetService()?.AllowedPlatformApis
+ ?? new List {AngleOptions.PlatformApi.DirectX9};
+
+ foreach (var platformApi in allowedApis)
{
- _angleApi = platformApi;
- break;
+ int dapi;
+ if (platformApi == AngleOptions.PlatformApi.DirectX9)
+ dapi = EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE;
+ else if (platformApi == AngleOptions.PlatformApi.DirectX11)
+ dapi = EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE;
+ else
+ continue;
+
+ _display = _egl.GetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, IntPtr.Zero,
+ new[] {EGL_PLATFORM_ANGLE_TYPE_ANGLE, dapi, EGL_NONE});
+ if (_display != IntPtr.Zero)
+ {
+ _angleApi = platformApi;
+ break;
+ }
}
+
+ if (_display == IntPtr.Zero)
+ throw new OpenGlException("Unable to create ANGLE display");
}
if (_display == IntPtr.Zero)
- throw new OpenGlException("Unable to create ANGLE display");
+ _display = _egl.GetDisplay(IntPtr.Zero);
+ }
+ else
+ {
+ if (_egl.GetPlatformDisplayEXT == null)
+ throw new OpenGlException("eglGetPlatformDisplayEXT is not supported by libegl");
+ _display = _egl.GetPlatformDisplayEXT(platformType, platformDisplay, attrs);
}
- if (_display == IntPtr.Zero)
- _display = _egl.GetDisplay(IntPtr.Zero);
-
if (_display == IntPtr.Zero)
throw OpenGlException.GetFormattedException("eglGetDisplay", _egl);
@@ -85,16 +98,14 @@ namespace Avalonia.OpenGL
{
if (!_egl.BindApi(cfg.Api))
continue;
-
+ foreach(var surfaceType in new[]{EGL_PBUFFER_BIT|EGL_WINDOW_BIT, EGL_WINDOW_BIT})
foreach(var stencilSize in new[]{8, 1, 0})
foreach (var depthSize in new []{8, 1, 0})
{
var attribs = new[]
{
- EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
-
+ EGL_SURFACE_TYPE, surfaceType,
EGL_RENDERABLE_TYPE, cfg.RenderableTypeBit,
-
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
@@ -108,6 +119,7 @@ namespace Avalonia.OpenGL
if (numConfigs == 0)
continue;
_contextAttributes = cfg.Attributes;
+ _surfaceType = surfaceType;
Type = cfg.Type;
}
}
@@ -126,8 +138,10 @@ namespace Avalonia.OpenGL
public GlDisplayType Type { get; }
public GlInterface GlInterface { get; }
public EglInterface EglInterface => _egl;
- public IGlContext CreateContext(IGlContext share)
+ public EglContext CreateContext(IGlContext share)
{
+ if((_surfaceType|EGL_PBUFFER_BIT) == 0)
+ throw new InvalidOperationException("Platform doesn't support PBUFFER surfaces");
var shareCtx = (EglContext)share;
var ctx = _egl.CreateContext(_display, _config, shareCtx?.Context ?? IntPtr.Zero, _contextAttributes);
if (ctx == IntPtr.Zero)
@@ -140,7 +154,17 @@ namespace Avalonia.OpenGL
});
if (surf == IntPtr.Zero)
throw OpenGlException.GetFormattedException("eglCreatePBufferSurface", _egl);
- var rv = new EglContext(this, _egl, ctx, surf);
+ var rv = new EglContext(this, _egl, ctx, new EglSurface(this, _egl, surf));
+ rv.MakeCurrent(null);
+ return rv;
+ }
+
+ public EglContext CreateContext(EglContext share, EglSurface offscreenSurface)
+ {
+ var ctx = _egl.CreateContext(_display, _config, share?.Context ?? IntPtr.Zero, _contextAttributes);
+ if (ctx == IntPtr.Zero)
+ throw OpenGlException.GetFormattedException("eglCreateContext", _egl);
+ var rv = new EglContext(this, _egl, ctx, offscreenSurface);
rv.MakeCurrent(null);
return rv;
}
diff --git a/src/Avalonia.OpenGL/EglInterface.cs b/src/Avalonia.OpenGL/EglInterface.cs
index 0a99778ddf..47088972a4 100644
--- a/src/Avalonia.OpenGL/EglInterface.cs
+++ b/src/Avalonia.OpenGL/EglInterface.cs
@@ -10,6 +10,11 @@ namespace Avalonia.OpenGL
public EglInterface() : base(Load())
{
+ }
+
+ public EglInterface(Func getProcAddress) : base(getProcAddress)
+ {
+
}
public EglInterface(string library) : base(Load(library))
diff --git a/src/Avalonia.OpenGL/GlInterface.cs b/src/Avalonia.OpenGL/GlInterface.cs
index f556949cfa..30f7d67152 100644
--- a/src/Avalonia.OpenGL/GlInterface.cs
+++ b/src/Avalonia.OpenGL/GlInterface.cs
@@ -39,7 +39,7 @@ namespace Avalonia.OpenGL
[GlEntryPoint("glClearStencil")]
public GlClearStencil ClearStencil { get; }
- public delegate void GlClearColor(int r, int g, int b, int a);
+ public delegate void GlClearColor(float r, float g, float b, float a);
[GlEntryPoint("glClearColor")]
public GlClearColor ClearColor { get; }
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/EvDevDevice.cs b/src/Linux/Avalonia.LinuxFramebuffer/EvDevDevice.cs
deleted file mode 100644
index f28dca81b8..0000000000
--- a/src/Linux/Avalonia.LinuxFramebuffer/EvDevDevice.cs
+++ /dev/null
@@ -1,88 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Runtime.InteropServices;
-
-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
index 78369a3648..5e2ba51caf 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
@@ -2,30 +2,35 @@
using System.Collections.Generic;
using Avalonia.Input;
using Avalonia.Input.Raw;
+using Avalonia.LinuxFramebuffer.Input;
+using Avalonia.LinuxFramebuffer.Output;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Threading;
namespace Avalonia.LinuxFramebuffer
{
- class FramebufferToplevelImpl : IEmbeddableWindowImpl
+ class FramebufferToplevelImpl : IEmbeddableWindowImpl, IScreenInfoProvider
{
- private readonly LinuxFramebuffer _fb;
+ private readonly IOutputBackend _outputBackend;
+ private readonly IInputBackend _inputBackend;
private bool _renderQueued;
public IInputRoot InputRoot { get; private set; }
- public FramebufferToplevelImpl(LinuxFramebuffer fb)
+ public FramebufferToplevelImpl(IOutputBackend outputBackend, IInputBackend inputBackend)
{
- _fb = fb;
+ _outputBackend = outputBackend;
+ _inputBackend = inputBackend;
Invalidate(default(Rect));
- var mice = new Mice(this, ClientSize.Width, ClientSize.Height);
- mice.Start();
- mice.Event += e => Input?.Invoke(e);
+ _inputBackend.Initialize(this, e => Input?.Invoke(e));
}
public IRenderer CreateRenderer(IRenderRoot root)
{
- return new ImmediateRenderer(root);
+ return new DeferredRenderer(root, AvaloniaLocator.Current.GetService())
+ {
+
+ };
}
public void Dispose()
@@ -36,19 +41,12 @@ namespace Avalonia.LinuxFramebuffer
public void Invalidate(Rect rect)
{
- if(_renderQueued)
- return;
- _renderQueued = true;
- Dispatcher.UIThread.Post(() =>
- {
- Paint?.Invoke(new Rect(default(Point), ClientSize));
- _renderQueued = false;
- });
}
public void SetInputRoot(IInputRoot inputRoot)
{
InputRoot = inputRoot;
+ _inputBackend.SetInputRoot(inputRoot);
}
public Point PointToClient(PixelPoint p) => p.ToPoint(1);
@@ -59,10 +57,10 @@ namespace Avalonia.LinuxFramebuffer
{
}
- public Size ClientSize => _fb.PixelSize;
- public IMouseDevice MouseDevice => LinuxFramebufferPlatform.MouseDevice;
+ public Size ClientSize => ScaledSize;
+ public IMouseDevice MouseDevice => new MouseDevice();
public double Scaling => 1;
- public IEnumerable