Browse Source

Fix NetDriver to also work well in Linux.

BDisp 8 months ago
parent
commit
8c5832f8f1

+ 11 - 1
Terminal.Gui/ConsoleDrivers/NetDriver/NetDriver.cs

@@ -756,7 +756,17 @@ internal class NetDriver : ConsoleDriver
 
                 _mainLoopDriver._netEvents._forceRead = true;
             }
-            _waitAnsiResponse.Wait (_ansiResponseTokenSource.Token);
+
+            if (!_ansiResponseTokenSource.IsCancellationRequested)
+            {
+                lock (ansiRequest._responseLock)
+                {
+                    _mainLoopDriver._waitForProbe.Set ();
+                    _mainLoopDriver._netEvents._waitForStart.Set ();
+                }
+
+                _waitAnsiResponse.Wait (_ansiResponseTokenSource.Token);
+            }
         }
         catch (OperationCanceledException)
         {

+ 71 - 19
Terminal.Gui/ConsoleDrivers/NetDriver/NetEvents.cs

@@ -1,13 +1,14 @@
 #nullable enable
-using System.Collections.Concurrent;
 using System.Diagnostics.CodeAnalysis;
 
 namespace Terminal.Gui;
 
 internal class NetEvents : IDisposable
 {
+    private readonly ManualResetEventSlim _inputReady = new (false);
     private CancellationTokenSource? _inputReadyCancellationTokenSource;
-    private readonly BlockingCollection<InputResult> _inputQueue = new (new ConcurrentQueue<InputResult> ());
+    internal readonly ManualResetEventSlim _waitForStart = new (false);
+    private readonly Queue<InputResult> _inputQueue = new ();
     private readonly ConsoleDriver _consoleDriver;
     private ConsoleKeyInfo []? _cki;
     private bool _isEscSeq;
@@ -30,14 +31,34 @@ internal class NetEvents : IDisposable
     {
         while (_inputReadyCancellationTokenSource is { })
         {
+            _waitForStart.Set ();
+
             try
             {
-                return _inputQueue.Take (_inputReadyCancellationTokenSource.Token);
+                if (!_inputReadyCancellationTokenSource.Token.IsCancellationRequested)
+                {
+                    if (_inputQueue.Count == 0)
+                    {
+                        _inputReady.Wait (_inputReadyCancellationTokenSource.Token);
+                    }
+                }
+
+                if (_inputQueue.Count > 0)
+                {
+                    return _inputQueue.Dequeue ();
+                }
             }
             catch (OperationCanceledException)
             {
                 return null;
             }
+            finally
+            {
+                if (_inputReadyCancellationTokenSource is { IsCancellationRequested: false })
+                {
+                    _inputReady.Reset ();
+                }
+            }
 
 #if PROCESS_REQUEST
             _neededProcessRequest = false;
@@ -102,6 +123,25 @@ internal class NetEvents : IDisposable
     {
         while (_inputReadyCancellationTokenSource is { IsCancellationRequested: false })
         {
+            try
+            {
+                if (!_forceRead)
+                {
+                    _waitForStart.Wait (_inputReadyCancellationTokenSource.Token);
+                }
+            }
+            catch (OperationCanceledException)
+            {
+                return;
+            }
+            finally
+            {
+                if (_inputReadyCancellationTokenSource is { IsCancellationRequested: false })
+                {
+                    _waitForStart.Reset ();
+                }
+            }
+
             try
             {
                 if (_inputQueue.Count == 0 || _forceRead)
@@ -125,7 +165,9 @@ internal class NetEvents : IDisposable
 
                         if (AnsiEscapeSequenceRequestUtils.IncompleteCkInfos is { })
                         {
+                            _cki = AnsiEscapeSequenceRequestUtils.ResizeArray (consoleKeyInfo, _cki);
                             _cki = AnsiEscapeSequenceRequestUtils.InsertArray (AnsiEscapeSequenceRequestUtils.IncompleteCkInfos, _cki);
+                            AnsiEscapeSequenceRequestUtils.IncompleteCkInfos = null;
                         }
 
                         if ((consoleKeyInfo.KeyChar == (char)KeyCode.Esc && !_isEscSeq)
@@ -205,6 +247,8 @@ internal class NetEvents : IDisposable
                         break;
                     }
                 }
+
+                _inputReady.Set ();
             }
             catch (OperationCanceledException)
             {
@@ -214,12 +258,12 @@ internal class NetEvents : IDisposable
 
         void ProcessMapConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
         {
-            _inputQueue.Add (
-                             new ()
-                             {
-                                 EventType = EventType.Key, ConsoleKeyInfo = AnsiEscapeSequenceRequestUtils.MapConsoleKeyInfo (consoleKeyInfo)
-                             }
-                            );
+            _inputQueue.Enqueue (
+                                 new ()
+                                 {
+                                     EventType = EventType.Key, ConsoleKeyInfo = AnsiEscapeSequenceRequestUtils.MapConsoleKeyInfo (consoleKeyInfo)
+                                 }
+                                );
             _isEscSeq = false;
         }
     }
@@ -265,6 +309,11 @@ internal class NetEvents : IDisposable
             try
             {
                 RequestWindowSize (_inputReadyCancellationTokenSource.Token);
+
+                if (_inputQueue.Count > 0)
+                {
+                    _inputReady.Set ();
+                }
             }
             catch (OperationCanceledException)
             {
@@ -289,12 +338,12 @@ internal class NetEvents : IDisposable
         int w = Math.Max (winWidth, 0);
         int h = Math.Max (winHeight, 0);
 
-        _inputQueue.Add (
-                         new ()
-                         {
-                             EventType = EventType.WindowSize, WindowSizeEvent = new () { Size = new (w, h) }
-                         }
-                        );
+        _inputQueue.Enqueue (
+                             new ()
+                             {
+                                 EventType = EventType.WindowSize, WindowSizeEvent = new () { Size = new (w, h) }
+                             }
+                            );
 
         return true;
     }
@@ -569,9 +618,9 @@ internal class NetEvents : IDisposable
     {
         var mouseEvent = new MouseEvent { Position = pos, ButtonState = buttonState };
 
-        _inputQueue.Add (
-                         new () { EventType = EventType.Mouse, MouseEvent = mouseEvent }
-                        );
+        _inputQueue.Enqueue (
+                             new () { EventType = EventType.Mouse, MouseEvent = mouseEvent }
+                            );
     }
 
     public enum EventType
@@ -684,7 +733,7 @@ internal class NetEvents : IDisposable
     {
         var inputResult = new InputResult { EventType = EventType.Key, ConsoleKeyInfo = cki };
 
-        _inputQueue.Add (inputResult);
+        _inputQueue.Enqueue (inputResult);
     }
 
     public void Dispose ()
@@ -693,6 +742,9 @@ internal class NetEvents : IDisposable
         _inputReadyCancellationTokenSource?.Dispose ();
         _inputReadyCancellationTokenSource = null;
 
+        _inputReady.Dispose ();
+        _waitForStart.Dispose ();
+
         try
         {
             // throws away any typeahead that has been typed by

+ 25 - 18
Terminal.Gui/ConsoleDrivers/NetDriver/NetMainLoop.cs

@@ -1,5 +1,4 @@
 #nullable enable
-using System.Collections.Concurrent;
 
 namespace Terminal.Gui;
 
@@ -16,9 +15,10 @@ internal class NetMainLoop : IMainLoopDriver
     internal Action<NetEvents.InputResult>? ProcessInput;
 
     private readonly ManualResetEventSlim _eventReady = new (false);
-    private readonly CancellationTokenSource _inputHandlerTokenSource = new ();
-    private readonly BlockingCollection<NetEvents.InputResult> _resultQueue = new (new ConcurrentQueue<NetEvents.InputResult> ());
+    internal readonly ManualResetEventSlim _waitForProbe = new (false);
     private readonly CancellationTokenSource _eventReadyTokenSource = new ();
+    private readonly CancellationTokenSource _inputHandlerTokenSource = new ();
+    private readonly Queue<NetEvents.InputResult> _resultQueue = new ();
     private MainLoop? _mainLoop;
 
     /// <summary>Initializes the class with the console driver.</summary>
@@ -48,6 +48,8 @@ internal class NetMainLoop : IMainLoopDriver
 
     bool IMainLoopDriver.EventsPending ()
     {
+        _waitForProbe.Set ();
+
         if (_resultQueue.Count > 0 || _mainLoop!.CheckTimersAndIdleHandlers (out int waitTimeout))
         {
             return true;
@@ -60,6 +62,13 @@ internal class NetMainLoop : IMainLoopDriver
                 // Note: ManualResetEventSlim.Wait will wait indefinitely if the timeout is -1. The timeout is -1 when there
                 // are no timers, but there IS an idle handler waiting.
                 _eventReady.Wait (waitTimeout, _eventReadyTokenSource.Token);
+
+                _eventReadyTokenSource.Token.ThrowIfCancellationRequested ();
+
+                if (!_eventReadyTokenSource.IsCancellationRequested)
+                {
+                    return _resultQueue.Count > 0 || _mainLoop.CheckTimersAndIdleHandlers (out _);
+                }
             }
         }
         catch (OperationCanceledException)
@@ -71,13 +80,6 @@ internal class NetMainLoop : IMainLoopDriver
             _eventReady.Reset ();
         }
 
-        _eventReadyTokenSource.Token.ThrowIfCancellationRequested ();
-
-        if (!_eventReadyTokenSource.IsCancellationRequested)
-        {
-            return _resultQueue.Count > 0 || _mainLoop.CheckTimersAndIdleHandlers (out _);
-        }
-
         // If cancellation was requested then always return true
         return true;
     }
@@ -86,10 +88,7 @@ internal class NetMainLoop : IMainLoopDriver
     {
         while (_resultQueue.Count > 0)
         {
-            if (_resultQueue.TryTake (out NetEvents.InputResult dequeueResult))
-            {
-                ProcessInput?.Invoke (dequeueResult);
-            }
+            ProcessInput?.Invoke (_resultQueue.Dequeue ());
         }
     }
 
@@ -101,8 +100,9 @@ internal class NetMainLoop : IMainLoopDriver
         _eventReadyTokenSource.Dispose ();
 
         _eventReady.Dispose ();
+        _waitForProbe.Dispose ();
 
-        _resultQueue.Dispose();
+        _resultQueue.Clear ();
         _netEvents?.Dispose ();
         _netEvents = null;
 
@@ -115,9 +115,9 @@ internal class NetMainLoop : IMainLoopDriver
         {
             try
             {
-                if (_inputHandlerTokenSource.IsCancellationRequested)
+                if (!_inputHandlerTokenSource.IsCancellationRequested && !_netEvents!._forceRead)
                 {
-                    return;
+                    _waitForProbe.Wait (_inputHandlerTokenSource.Token);
                 }
 
                 if (_resultQueue?.Count == 0 || _netEvents!._forceRead)
@@ -126,7 +126,7 @@ internal class NetMainLoop : IMainLoopDriver
 
                     if (result.HasValue)
                     {
-                        _resultQueue?.Add (result.Value);
+                        _resultQueue?.Enqueue (result.Value);
                     }
                 }
 
@@ -139,6 +139,13 @@ internal class NetMainLoop : IMainLoopDriver
             {
                 return;
             }
+            finally
+            {
+                if (_inputHandlerTokenSource is { IsCancellationRequested: false })
+                {
+                    _waitForProbe.Reset ();
+                }
+            }
         }
     }
 }