Browse Source

Fixed platform threading for fbdev

Nikita Tsukanov 6 years ago
parent
commit
e9baedcbfc

+ 30 - 55
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<Action> _actions = new Queue<Action>();
 
         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<int>();
-                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)

+ 1 - 1
src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs

@@ -54,7 +54,7 @@ namespace Avalonia.DesignerSupport.Remote
                 .Bind<IPlatformSettings>().ToConstant(instance)
                 .Bind<IPlatformThreadingInterface>().ToConstant(threading)
                 .Bind<IRenderLoop>().ToConstant(new RenderLoop())
-                .Bind<IRenderTimer>().ToConstant(threading)
+                .Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
                 .Bind<ISystemDialogImpl>().ToSingleton<SystemDialogsStub>()
                 .Bind<IWindowingPlatform>().ToConstant(instance)
                 .Bind<IPlatformIconLoader>().ToSingleton<IconLoaderStub>()

+ 2 - 10
src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs

@@ -26,7 +26,7 @@ namespace Avalonia.LinuxFramebuffer
 
         public IRenderer CreateRenderer(IRenderRoot root)
         {
-            return new ImmediateRenderer(root);
+            return new DeferredRenderer(root, AvaloniaLocator.Current.GetService<IRenderLoop>());
         }
 
         public void Dispose()
@@ -37,14 +37,6 @@ 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)
@@ -62,7 +54,7 @@ namespace Avalonia.LinuxFramebuffer
         }
 
         public Size ClientSize => _fb.PixelSize;
-        public IMouseDevice MouseDevice => LinuxFramebufferPlatform.MouseDevice;
+        public IMouseDevice MouseDevice => new MouseDevice();
         public double Scaling => 1;
         public IEnumerable<object> Surfaces => new object[] {_fb};
         public Action<RawInputEventArgs> Input { get; set; }

+ 23 - 10
src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs

@@ -40,7 +40,9 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput
                 libinput_path_add_device(ctx, f);
             while (true)
             {
+                
                 IntPtr ev;
+                libinput_dispatch(ctx);
                 while ((ev = libinput_get_event(ctx)) != IntPtr.Zero)
                 {
                     
@@ -50,25 +52,36 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput
                         HandleTouch(ev, type);
                     
                     libinput_event_destroy(ev);
+                    libinput_dispatch(ctx);
                 }
-                libinput_dispatch(ctx);
+
+                pollfd pfd = new pollfd {fd = fd, events = 1};
+                NativeUnsafeMethods.poll(&pfd, new IntPtr(1), 10);
             }
         }
 
         private void ScheduleInput(RawInputEventArgs ev)
         {
-            _inputQueue.Enqueue(ev);
-            if (_inputQueue.Count == 1)
+            lock (_inputQueue)
             {
-                Dispatcher.UIThread.Post(() =>
+                _inputQueue.Enqueue(ev);
+                if (_inputQueue.Count == 1)
                 {
-                    while (_inputQueue.Count > 0)
+                    Dispatcher.UIThread.Post(() =>
                     {
-                        Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
-                        var dequeuedEvent = _inputQueue.Dequeue();
-                        _onInput?.Invoke(dequeuedEvent);
-                    }
-                }, DispatcherPriority.Input);
+                        while (true)
+                        {
+                            Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
+                            RawInputEventArgs dequeuedEvent = null;
+                            lock(_inputQueue)
+                                if (_inputQueue.Count != 0)
+                                    dequeuedEvent = _inputQueue.Dequeue();
+                            if (dequeuedEvent == null)
+                                return;
+                            _onInput?.Invoke(dequeuedEvent);
+                        }
+                    }, DispatcherPriority.Input);
+                }
             }
         }
 

+ 8 - 6
src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs

@@ -18,8 +18,6 @@ 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 InternalPlatformThreadingInterface Threading;
@@ -33,13 +31,15 @@ namespace Avalonia.LinuxFramebuffer
         {
             Threading = new InternalPlatformThreadingInterface();
             AvaloniaLocator.CurrentMutable
+                .Bind<IPlatformThreadingInterface>().ToConstant(Threading)
+                .Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
+                .Bind<IRenderLoop>().ToConstant(new RenderLoop())
                 .Bind<IStandardCursorFactory>().ToTransient<CursorFactoryStub>()
-                .Bind<IKeyboardDevice>().ToConstant(KeyboardDevice)
+                .Bind<IKeyboardDevice>().ToConstant(new KeyboardDevice())
                 .Bind<IPlatformSettings>().ToSingleton<PlatformSettings>()
-                .Bind<IPlatformThreadingInterface>().ToConstant(Threading)
                 .Bind<IRenderLoop>().ToConstant(new RenderLoop())
-                .Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
-                .Bind<IRenderTimer>().ToConstant(Threading);
+                .Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>();
+
         }
 
         internal static LinuxFramebufferLifetime Initialize<T>(T builder, string fbdev = null) where T : AppBuilderBase<T>, new()
@@ -73,7 +73,9 @@ namespace Avalonia.LinuxFramebuffer
                     var tl = new EmbeddableControlRoot(new FramebufferToplevelImpl(_fb, new LibInputBackend()));
                     tl.Prepare();
                     _topLevel = tl;
+                    _topLevel.Renderer.Start();
                 }
+
                 _topLevel.Content = value;
             }
         }

+ 11 - 0
src/Linux/Avalonia.LinuxFramebuffer/NativeUnsafeMethods.cs

@@ -33,6 +33,10 @@ namespace Avalonia.LinuxFramebuffer
         [DllImport("libc", EntryPoint = "select", SetLastError = true)]
         public static extern int select(int nfds, void* rfds, void* wfds, void* exfds, IntPtr* timevals);
 
+
+        [DllImport("libc", EntryPoint = "poll", SetLastError = true)]
+        public static extern int poll(pollfd* fds, IntPtr nfds, int timeout);
+
         [DllImport("libevdev.so.2", EntryPoint = "libevdev_new_from_fd", SetLastError = true)]
         public static extern int libevdev_new_from_fd(int fd, out IntPtr dev);
 
@@ -48,6 +52,13 @@ namespace Avalonia.LinuxFramebuffer
         public static extern input_absinfo* libevdev_get_abs_info(IntPtr dev, int code);
     }
 
+    [StructLayout(LayoutKind.Sequential)]
+    struct pollfd {
+        public int   fd;         /* file descriptor */
+        public short events;     /* requested events */
+        public short revents;    /* returned events */
+    };
+    
     enum FbIoCtl : uint
     {
         FBIOGET_VSCREENINFO = 0x4600,