Browse Source

Add ProcessAnsiRequestHandler method task.

BDisp 8 tháng trước cách đây
mục cha
commit
54f62645a2

+ 104 - 55
Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs

@@ -1,5 +1,6 @@
 #nullable enable
 
+using System.Collections.Concurrent;
 using System.Diagnostics;
 
 namespace Terminal.Gui;
@@ -12,6 +13,79 @@ namespace Terminal.Gui;
 /// </remarks>
 public abstract class ConsoleDriver
 {
+    private readonly ManualResetEventSlim _waitAnsiRequest = new (false);
+    private readonly ManualResetEventSlim _waitAnsiResponse = new (false);
+    private readonly CancellationTokenSource? _ansiRequestTokenSource = new ();
+    private readonly ConcurrentQueue<AnsiEscapeSequenceRequest> _requestQueue = new ();
+    private readonly ConcurrentQueue<AnsiEscapeSequenceRequest> _responseQueue = new ();
+    private IMainLoopDriver? _mainLoopDriver;
+
+    internal void ProcessAnsiRequestHandler ()
+    {
+        while (_ansiRequestTokenSource is { IsCancellationRequested: false})
+        {
+            try
+            {
+                if (_requestQueue.Count == 0)
+                {
+                    try
+                    {
+                        _waitAnsiRequest.Wait (_ansiRequestTokenSource.Token);
+                    }
+                    catch (OperationCanceledException)
+                    {
+                        return;
+                    }
+
+                    _waitAnsiRequest.Reset ();
+                }
+
+                while (_requestQueue.TryDequeue (out AnsiEscapeSequenceRequest? ansiRequest))
+                {
+                    try
+                    {
+                        lock (ansiRequest._responseLock)
+                        {
+                            AnsiEscapeSequenceRequest? request = ansiRequest;
+
+                            ansiRequest.ResponseFromInput += (s, e) =>
+                                                             {
+                                                                 Debug.Assert (s == request);
+                                                                 Debug.Assert (e == request.AnsiEscapeSequenceResponse);
+
+                                                                 _responseQueue.Enqueue (request);
+
+                                                                 _waitAnsiResponse.Set ();
+                                                             };
+
+                            AnsiEscapeSequenceRequests.Add (ansiRequest);
+
+                            WriteRaw (ansiRequest.Request);
+
+                            _mainLoopDriver!._forceRead = true;
+                        }
+
+                        if (!_ansiRequestTokenSource.IsCancellationRequested)
+                        {
+                            _mainLoopDriver._waitForInput.Set ();
+
+                            _waitAnsiRequest.Wait (_ansiRequestTokenSource.Token);
+                        }
+                    }
+                    catch (OperationCanceledException)
+                    {
+                        return;
+                    }
+
+                }
+            }
+            catch (OperationCanceledException)
+            {
+                return;
+            }
+        }
+    }
+
     /// <summary>
     ///     Set this to true in any unit tests that attempt to test drivers other than FakeDriver.
     ///     <code>
@@ -32,81 +106,56 @@ public abstract class ConsoleDriver
 
     #region ANSI Esc Sequence Handling
 
-    private readonly ManualResetEventSlim _waitAnsiResponse = new (false);
-    private CancellationTokenSource? _ansiResponseTokenSource;
-
-    // QUESTION: Should this be virtual with a default implementation that does the common stuff?
-    // QUESTION: Looking at the implementations of this method, there is TONs of duplicated code.
-    // QUESTION: We should figure out how to find just the things that are unique to each driver and
-    // QUESTION: create more fine-grained APIs to handle those.
     /// <summary>
-    ///     Provide handling for the terminal write ANSI escape sequence request.
+    ///     Provide unique handling for all the terminal write ANSI escape sequence request.
     /// </summary>
     /// <param name="mainLoopDriver">The <see cref="IMainLoopDriver"/> object.</param>
     /// <param name="ansiRequest">The <see cref="AnsiEscapeSequenceRequest"/> object.</param>
-    /// <returns>The request response.</returns>
-    public virtual bool TryWriteAnsiRequest (IMainLoopDriver mainLoopDriver, ref AnsiEscapeSequenceRequest ansiRequest)
+    /// <returns><see languard="true"/> if the request response is valid, <see languard="false"/> otherwise.</returns>
+    public bool TryWriteAnsiRequest (IMainLoopDriver mainLoopDriver, ref AnsiEscapeSequenceRequest ansiRequest)
     {
-        if (mainLoopDriver is null)
+        ArgumentNullException.ThrowIfNull (mainLoopDriver, nameof (mainLoopDriver));
+        ArgumentNullException.ThrowIfNull (ansiRequest, nameof (ansiRequest));
+
+        lock (ansiRequest._responseLock)
         {
-            return false;
-        }
+            _mainLoopDriver = mainLoopDriver;
+            _requestQueue.Enqueue (ansiRequest);
 
-        _ansiResponseTokenSource ??= new ();
+            _waitAnsiRequest.Set ();
+        }
 
         try
         {
-            lock (ansiRequest._responseLock)
-            {
-                AnsiEscapeSequenceRequest? request = ansiRequest;
+            _waitAnsiResponse.Wait (_ansiRequestTokenSource!.Token);
 
-                ansiRequest.ResponseFromInput += (s, e) =>
-                                                 {
-                                                     Debug.Assert (s == request);
-                                                     Debug.Assert (e == request.AnsiEscapeSequenceResponse);
-
-                                                     _waitAnsiResponse.Set ();
-                                                 };
-
-                AnsiEscapeSequenceRequests.Add (ansiRequest);
-
-                WriteRaw (ansiRequest.Request);
+            _waitAnsiResponse.Reset ();
 
-                mainLoopDriver._forceRead = true;
-            }
+            _responseQueue.TryDequeue (out _);
 
-            if (!_ansiResponseTokenSource.IsCancellationRequested)
+            lock (ansiRequest._responseLock)
             {
-                mainLoopDriver._waitForInput.Set ();
+                _mainLoopDriver._forceRead = false;
 
-                _waitAnsiResponse.Wait (_ansiResponseTokenSource.Token);
-            }
-        }
-        catch (OperationCanceledException)
-        {
-            return false;
-        }
-
-        lock (ansiRequest._responseLock)
-        {
-            mainLoopDriver._forceRead = false;
-
-            if (AnsiEscapeSequenceRequests.Statuses.TryPeek (out AnsiEscapeSequenceRequestStatus? request))
-            {
-                if (AnsiEscapeSequenceRequests.Statuses.Count > 0
-                    && string.IsNullOrEmpty (request.AnsiRequest.AnsiEscapeSequenceResponse?.Response))
+                if (AnsiEscapeSequenceRequests.Statuses.TryPeek (out AnsiEscapeSequenceRequestStatus? request))
                 {
-                    lock (request.AnsiRequest._responseLock)
+                    if (AnsiEscapeSequenceRequests.Statuses.Count > 0
+                        && string.IsNullOrEmpty (request.AnsiRequest.AnsiEscapeSequenceResponse?.Response))
                     {
-                        // Bad request or no response at all
-                        AnsiEscapeSequenceRequests.Statuses.TryDequeue (out _);
+                        lock (request.AnsiRequest._responseLock)
+                        {
+                            // Bad request or no response at all
+                            AnsiEscapeSequenceRequests.Statuses.TryDequeue (out _);
+                        }
                     }
                 }
-            }
 
-            _waitAnsiResponse.Reset ();
-
-            return ansiRequest.AnsiEscapeSequenceResponse is { Valid: true };
+                return ansiRequest.AnsiEscapeSequenceResponse is { Valid: true };
+            }
+        }
+        catch (OperationCanceledException)
+        {
+            return false;
         }
     }
 

+ 2 - 0
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -679,6 +679,8 @@ internal class CursesDriver : ConsoleDriver
             {
                 Curses.refresh ();
             }
+
+            Task.Run (ProcessAnsiRequestHandler);
         }
 
         return new (_mainLoopDriver);

+ 0 - 3
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs

@@ -392,9 +392,6 @@ public class FakeDriver : ConsoleDriver
         MockKeyPressedHandler (new ConsoleKeyInfo (keyChar, key, shift, alt, control));
     }
 
-    /// <inheritdoc />
-    public override bool TryWriteAnsiRequest (IMainLoopDriver mainLoopDriver, ref AnsiEscapeSequenceRequest ansiRequest) { throw new NotImplementedException (); }
-
     /// <inheritdoc />
     internal override void WriteRaw (string ansi) { throw new NotImplementedException (); }
 

+ 5 - 0
Terminal.Gui/ConsoleDrivers/NetDriver/NetDriver.cs

@@ -294,6 +294,11 @@ internal class NetDriver : ConsoleDriver
         _mainLoopDriver = new (this);
         _mainLoopDriver.ProcessInput = ProcessInput;
 
+        if (!RunningUnitTests)
+        {
+            Task.Run (ProcessAnsiRequestHandler);
+        }
+
         return new (_mainLoopDriver);
     }
 

+ 2 - 0
Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsDriver.cs

@@ -481,6 +481,8 @@ internal class WindowsDriver : ConsoleDriver
         if (!RunningUnitTests)
         {
             WinConsole?.SetInitialCursorVisibility ();
+
+            Task.Run (ProcessAnsiRequestHandler);
         }
 
         return new MainLoop (_mainLoopDriver);