Browse Source

Improving WriteAnsi method to return the response.

BDisp 9 months ago
parent
commit
884011e99c

+ 10 - 28
Terminal.Gui/ConsoleDrivers/AnsiEscapeSequence/AnsiEscapeSequenceRequest.cs

@@ -16,6 +16,11 @@ public class AnsiEscapeSequenceRequest
     /// </summary>
     /// </summary>
     public required string Request { get; init; }
     public required string Request { get; init; }
 
 
+    /// <summary>
+    ///     Response received from the request.
+    /// </summary>
+    public string Response { get; internal set; } = string.Empty;
+
     /// <summary>
     /// <summary>
     ///     Invoked when the console responds with an ANSI response code that matches the
     ///     Invoked when the console responds with an ANSI response code that matches the
     ///     <see cref="Terminator"/>
     ///     <see cref="Terminator"/>
@@ -53,7 +58,6 @@ public class AnsiEscapeSequenceRequest
     /// <returns>A <see cref="AnsiEscapeSequenceResponse"/> with the response, error, terminator and value.</returns>
     /// <returns>A <see cref="AnsiEscapeSequenceResponse"/> with the response, error, terminator and value.</returns>
     public static bool TryExecuteAnsiRequest (AnsiEscapeSequenceRequest ansiRequest, out AnsiEscapeSequenceResponse result)
     public static bool TryExecuteAnsiRequest (AnsiEscapeSequenceRequest ansiRequest, out AnsiEscapeSequenceResponse result)
     {
     {
-        var response = new StringBuilder ();
         var error = new StringBuilder ();
         var error = new StringBuilder ();
         var savedIsReportingMouseMoves = false;
         var savedIsReportingMouseMoves = false;
         ConsoleDriver? driver = null;
         ConsoleDriver? driver = null;
@@ -73,35 +77,13 @@ public class AnsiEscapeSequenceRequest
             driver!.IsSuspendRead = true;
             driver!.IsSuspendRead = true;
 
 
             // Send the ANSI escape sequence
             // Send the ANSI escape sequence
-            driver.WriteAnsi (ansiRequest.Request);
-            Console.Out.Flush (); // Ensure the request is sent
-
-            // Read the response from stdin (response should come back as input)
-            Thread.Sleep (100); // Allow time for the terminal to respond
-
-            // Read input until no more characters are available or the terminator is encountered
-            while (Console.KeyAvailable)
-            {
-                // Peek the next key
-                ConsoleKeyInfo keyInfo = Console.ReadKey (true); // true to not display on the console
-
-                // Append the current key to the response
-                response.Append (keyInfo.KeyChar);
-
-                // Read until no key is available if no terminator was specified or
-                // check if the key is terminator (ANSI escape sequence ends)
-                if (!string.IsNullOrEmpty (ansiRequest.Terminator) && keyInfo.KeyChar == ansiRequest.Terminator [^1])
-                {
-                    // Break out of the loop when terminator is found
-                    break;
-                }
-            }
+            ansiRequest.Response = driver.WriteAnsi (ansiRequest);
 
 
             if (string.IsNullOrEmpty (ansiRequest.Terminator))
             if (string.IsNullOrEmpty (ansiRequest.Terminator))
             {
             {
                 error.AppendLine ("Terminator request is empty.");
                 error.AppendLine ("Terminator request is empty.");
             }
             }
-            else if (!response.ToString ().EndsWith (ansiRequest.Terminator [^1]))
+            else if (!ansiRequest.Response.EndsWith (ansiRequest.Terminator [^1]))
             {
             {
                 throw new InvalidOperationException ($"Terminator doesn't ends with: '{ansiRequest.Terminator [^1]}'");
                 throw new InvalidOperationException ($"Terminator doesn't ends with: '{ansiRequest.Terminator [^1]}'");
             }
             }
@@ -114,7 +96,7 @@ public class AnsiEscapeSequenceRequest
         {
         {
             if (string.IsNullOrEmpty (error.ToString ()))
             if (string.IsNullOrEmpty (error.ToString ()))
             {
             {
-                (string? c1Control, string? code, values, string? terminator) = EscSeqUtils.GetEscapeResult (response.ToString ().ToCharArray ());
+                (string? c1Control, string? code, values, string? terminator) = EscSeqUtils.GetEscapeResult (ansiRequest.Response.ToCharArray ());
             }
             }
 
 
             if (savedIsReportingMouseMoves)
             if (savedIsReportingMouseMoves)
@@ -126,8 +108,8 @@ public class AnsiEscapeSequenceRequest
 
 
         AnsiEscapeSequenceResponse ansiResponse = new ()
         AnsiEscapeSequenceResponse ansiResponse = new ()
         {
         {
-            Response = response.ToString (), Error = error.ToString (),
-            Terminator = string.IsNullOrEmpty (response.ToString ()) ? "" : response.ToString () [^1].ToString (), Value = values [0]
+            Response = ansiRequest.Response, Error = error.ToString (),
+            Terminator = string.IsNullOrEmpty (ansiRequest.Response) ? "" : ansiRequest.Response [^1].ToString (), Value = values [0]
         };
         };
 
 
         // Invoke the event if it's subscribed
         // Invoke the event if it's subscribed

+ 32 - 3
Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs

@@ -629,17 +629,22 @@ public abstract class ConsoleDriver
     public abstract void StopReportingMouseMoves ();
     public abstract void StopReportingMouseMoves ();
 
 
     /// <summary>
     /// <summary>
-    /// Provide handling for the terminal write ANSI escape sequence.
+    /// Provide handling for the terminal write ANSI escape sequence request.
     /// </summary>
     /// </summary>
-    /// <param name="ansi">The ANSI escape sequence.</param>
+    /// <param name="ansiRequest">The <see cref="AnsiEscapeSequenceRequest"/> object.</param>
     /// <returns></returns>
     /// <returns></returns>
-    public abstract bool WriteAnsi (string ansi);
+    public abstract string WriteAnsi (AnsiEscapeSequenceRequest ansiRequest);
 
 
     internal bool WriteAnsiDefault (string ansi)
     internal bool WriteAnsiDefault (string ansi)
     {
     {
         try
         try
         {
         {
             Console.Out.Write (ansi);
             Console.Out.Write (ansi);
+            Console.Out.Flush (); // Ensure the request is sent
+
+            // Read the response from stdin (response should come back as input)
+            Thread.Sleep (100); // Allow time for the terminal to respond
+
         }
         }
         catch (Exception)
         catch (Exception)
         {
         {
@@ -649,6 +654,30 @@ public abstract class ConsoleDriver
         return true;
         return true;
     }
     }
 
 
+    internal string ReadAnsiDefault (AnsiEscapeSequenceRequest ansiRequest)
+    {
+        var response = new StringBuilder ();
+
+        while (Console.KeyAvailable)
+        {
+            // Peek the next key
+            ConsoleKeyInfo keyInfo = Console.ReadKey (true); // true to not display on the console
+
+            // Append the current key to the response
+            response.Append (keyInfo.KeyChar);
+
+            // Read until no key is available if no terminator was specified or
+            // check if the key is terminator (ANSI escape sequence ends)
+            if (!string.IsNullOrEmpty (ansiRequest.Terminator) && keyInfo.KeyChar == ansiRequest.Terminator [^1])
+            {
+                // Break out of the loop when terminator is found
+                break;
+            }
+        }
+
+        return response.ToString ();
+    }
+
     #endregion
     #endregion
 }
 }
 
 

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

@@ -216,9 +216,14 @@ internal class CursesDriver : ConsoleDriver
     }
     }
 
 
     /// <inheritdoc />
     /// <inheritdoc />
-    public override bool WriteAnsi (string ansi)
+    public override string WriteAnsi (AnsiEscapeSequenceRequest ansiRequest)
     {
     {
-        return WriteAnsiDefault (ansi);
+        if (WriteAnsiDefault (ansiRequest.Request))
+        {
+            return ReadAnsiDefault (ansiRequest);
+        }
+
+        return string.Empty;
     }
     }
 
 
     public override void Suspend ()
     public override void Suspend ()

+ 20 - 52
Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqReq.cs

@@ -11,17 +11,7 @@ public class EscSeqReqStatus
 {
 {
     /// <summary>Creates a new state of escape sequence request.</summary>
     /// <summary>Creates a new state of escape sequence request.</summary>
     /// <param name="ansiRequest">The <see cref="AnsiEscapeSequenceRequest"/> object.</param>
     /// <param name="ansiRequest">The <see cref="AnsiEscapeSequenceRequest"/> object.</param>
-    public EscSeqReqStatus (AnsiEscapeSequenceRequest ansiRequest)
-    {
-        AnsiRequest = ansiRequest;
-        NumRequests = NumOutstanding = 1;
-    }
-
-    /// <summary>Gets the number of unfinished requests.</summary>
-    public int NumOutstanding { get; set; }
-
-    /// <summary>Gets the number of requests.</summary>
-    public int NumRequests { get; set; }
+    public EscSeqReqStatus (AnsiEscapeSequenceRequest ansiRequest) { AnsiRequest = ansiRequest; }
 
 
     /// <summary>Gets the Escape Sequence Terminator (e.g. ESC[8t ... t is the terminator).</summary>
     /// <summary>Gets the Escape Sequence Terminator (e.g. ESC[8t ... t is the terminator).</summary>
     public AnsiEscapeSequenceRequest AnsiRequest { get; }
     public AnsiEscapeSequenceRequest AnsiRequest { get; }
@@ -34,9 +24,6 @@ public class EscSeqReqStatus
 /// </summary>
 /// </summary>
 public class EscSeqRequests
 public class EscSeqRequests
 {
 {
-    /// <summary>Gets the <see cref="EscSeqReqStatus"/> list.</summary>
-    public List<EscSeqReqStatus> Statuses { get; } = new ();
-
     /// <summary>
     /// <summary>
     ///     Adds a new request for the ANSI Escape Sequence defined by <paramref name="ansiRequest"/>. Adds a
     ///     Adds a new request for the ANSI Escape Sequence defined by <paramref name="ansiRequest"/>. Adds a
     ///     <see cref="EscSeqReqStatus"/> instance to <see cref="Statuses"/> list.
     ///     <see cref="EscSeqReqStatus"/> instance to <see cref="Statuses"/> list.
@@ -46,21 +33,10 @@ public class EscSeqRequests
     {
     {
         lock (Statuses)
         lock (Statuses)
         {
         {
-            EscSeqReqStatus? found = Statuses.Find (x => x.AnsiRequest.Terminator == ansiRequest.Terminator);
-
-            if (found is null)
-            {
-                Statuses.Add (new (ansiRequest));
-            }
-            else if (found.NumOutstanding < found.NumRequests)
-            {
-                found.NumOutstanding = Math.Min (found.NumOutstanding + 1, found.NumRequests);
-            }
-            else
-            {
-                found.NumRequests++;
-                found.NumOutstanding++;
-            }
+            Statuses.Enqueue (new (ansiRequest));
+            Console.Out.Write (ansiRequest.Request);
+            Console.Out.Flush ();
+            Thread.Sleep (100); // Allow time for the terminal to respond
         }
         }
     }
     }
 
 
@@ -75,10 +51,18 @@ public class EscSeqRequests
     {
     {
         lock (Statuses)
         lock (Statuses)
         {
         {
-            EscSeqReqStatus? found = Statuses.Find (x => x.AnsiRequest.Terminator == terminator);
-            seqReqStatus = found;
+            Statuses.TryPeek (out seqReqStatus);
+
+            var result = seqReqStatus?.AnsiRequest.Terminator == terminator;
 
 
-            return found is { NumOutstanding: > 0 };
+            if (result)
+            {
+                return true;
+            }
+
+            seqReqStatus = null;
+
+            return false;
         }
         }
     }
     }
 
 
@@ -93,26 +77,10 @@ public class EscSeqRequests
     {
     {
         lock (Statuses)
         lock (Statuses)
         {
         {
-            EscSeqReqStatus? found = Statuses.Find (x => x == seqReqStatus);
-
-            if (found is null)
-            {
-                return;
-            }
-
-            if (found is { NumOutstanding: 0 })
-            {
-                Statuses.Remove (found);
-            }
-            else if (found is { NumOutstanding: > 0 })
-            {
-                found.NumOutstanding--;
-
-                if (found.NumOutstanding == 0)
-                {
-                    Statuses.Remove (found);
-                }
-            }
+            Statuses.Dequeue ();
         }
         }
     }
     }
+
+    /// <summary>Gets the <see cref="EscSeqReqStatus"/> list.</summary>
+    public Queue<EscSeqReqStatus> Statuses { get; } = new ();
 }
 }

+ 2 - 2
Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs

@@ -174,7 +174,7 @@ public static class EscSeqUtils
     /// <param name="seqReqStatus">The <see cref="EscSeqReqStatus"/> object.</param>
     /// <param name="seqReqStatus">The <see cref="EscSeqReqStatus"/> object.</param>
     /// <param name="continuousButtonPressedHandler">The handler that will process the event.</param>
     /// <param name="continuousButtonPressedHandler">The handler that will process the event.</param>
     public static void DecodeEscSeq (
     public static void DecodeEscSeq (
-        EscSeqRequests escSeqRequests,
+        EscSeqRequests? escSeqRequests,
         ref ConsoleKeyInfo newConsoleKeyInfo,
         ref ConsoleKeyInfo newConsoleKeyInfo,
         ref ConsoleKey key,
         ref ConsoleKey key,
         ConsoleKeyInfo [] cki,
         ConsoleKeyInfo [] cki,
@@ -497,7 +497,7 @@ public static class EscSeqUtils
     // PERF: This is expensive
     // PERF: This is expensive
     public static char [] GetKeyCharArray (ConsoleKeyInfo [] cki)
     public static char [] GetKeyCharArray (ConsoleKeyInfo [] cki)
     {
     {
-        char [] kChar = { };
+        char [] kChar = [];
         var length = 0;
         var length = 0;
 
 
         foreach (ConsoleKeyInfo kc in cki)
         foreach (ConsoleKeyInfo kc in cki)

+ 7 - 2
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs

@@ -415,9 +415,14 @@ public class FakeDriver : ConsoleDriver
     public override void StopReportingMouseMoves () { throw new NotImplementedException (); }
     public override void StopReportingMouseMoves () { throw new NotImplementedException (); }
 
 
     /// <inheritdoc />
     /// <inheritdoc />
-    public override bool WriteAnsi (string ansi)
+    public override string WriteAnsi (AnsiEscapeSequenceRequest ansiRequest)
     {
     {
-        return WriteAnsiDefault (ansi);
+        if (WriteAnsiDefault (ansiRequest.Request))
+        {
+            return ReadAnsiDefault (ansiRequest);
+        }
+
+        return string.Empty;
     }
     }
 
 
     public void SetBufferSize (int width, int height)
     public void SetBufferSize (int width, int height)

+ 101 - 59
Terminal.Gui/ConsoleDrivers/NetDriver.cs

@@ -446,7 +446,17 @@ internal class NetEvents : IDisposable
 
 
         if (seqReqStatus is { })
         if (seqReqStatus is { })
         {
         {
-            HandleRequestResponseEvent (c1Control, code, values, terminating);
+            //HandleRequestResponseEvent (c1Control, code, values, terminating);
+            StringBuilder sb = new ();
+
+            foreach (ConsoleKeyInfo keyChar in cki)
+            {
+                sb.Append (keyChar.KeyChar);
+            }
+
+            seqReqStatus.AnsiRequest.Response = sb.ToString ();
+
+            ((NetDriver)_consoleDriver)._waitAnsiResponse.Set ();
 
 
             return;
             return;
         }
         }
@@ -590,64 +600,64 @@ internal class NetEvents : IDisposable
 
 
     private Point _lastCursorPosition;
     private Point _lastCursorPosition;
 
 
-    private void HandleRequestResponseEvent (string c1Control, string code, string [] values, string terminating)
-    {
-        if (terminating ==
-
-            // BUGBUG: I can't find where we send a request for cursor position (ESC[?6n), so I'm not sure if this is needed.
-            // The observation is correct because the response isn't immediate and this is useless
-            EscSeqUtils.CSI_RequestCursorPositionReport.Terminator)
-        {
-            var point = new Point { X = int.Parse (values [1]) - 1, Y = int.Parse (values [0]) - 1 };
-
-            if (_lastCursorPosition.Y != point.Y)
-            {
-                _lastCursorPosition = point;
-                var eventType = EventType.WindowPosition;
-                var winPositionEv = new WindowPositionEvent { CursorPosition = point };
-
-                _inputQueue.Enqueue (
-                                     new InputResult { EventType = eventType, WindowPositionEvent = winPositionEv }
-                                    );
-            }
-            else
-            {
-                return;
-            }
-        }
-        else if (terminating == EscSeqUtils.CSI_ReportTerminalSizeInChars.Terminator)
-        {
-            if (values [0] == EscSeqUtils.CSI_ReportTerminalSizeInChars.Value)
-            {
-                EnqueueWindowSizeEvent (
-                                        Math.Max (int.Parse (values [1]), 0),
-                                        Math.Max (int.Parse (values [2]), 0),
-                                        Math.Max (int.Parse (values [1]), 0),
-                                        Math.Max (int.Parse (values [2]), 0)
-                                       );
-            }
-            else
-            {
-                EnqueueRequestResponseEvent (c1Control, code, values, terminating);
-            }
-        }
-        else
-        {
-            EnqueueRequestResponseEvent (c1Control, code, values, terminating);
-        }
-
-        _inputReady.Set ();
-    }
+    //private void HandleRequestResponseEvent (string c1Control, string code, string [] values, string terminating)
+    //{
+    //    if (terminating ==
+
+    //        // BUGBUG: I can't find where we send a request for cursor position (ESC[?6n), so I'm not sure if this is needed.
+    //        // The observation is correct because the response isn't immediate and this is useless
+    //        EscSeqUtils.CSI_RequestCursorPositionReport.Terminator)
+    //    {
+    //        var point = new Point { X = int.Parse (values [1]) - 1, Y = int.Parse (values [0]) - 1 };
+
+    //        if (_lastCursorPosition.Y != point.Y)
+    //        {
+    //            _lastCursorPosition = point;
+    //            var eventType = EventType.WindowPosition;
+    //            var winPositionEv = new WindowPositionEvent { CursorPosition = point };
+
+    //            _inputQueue.Enqueue (
+    //                                 new InputResult { EventType = eventType, WindowPositionEvent = winPositionEv }
+    //                                );
+    //        }
+    //        else
+    //        {
+    //            return;
+    //        }
+    //    }
+    //    else if (terminating == EscSeqUtils.CSI_ReportTerminalSizeInChars.Terminator)
+    //    {
+    //        if (values [0] == EscSeqUtils.CSI_ReportTerminalSizeInChars.Value)
+    //        {
+    //            EnqueueWindowSizeEvent (
+    //                                    Math.Max (int.Parse (values [1]), 0),
+    //                                    Math.Max (int.Parse (values [2]), 0),
+    //                                    Math.Max (int.Parse (values [1]), 0),
+    //                                    Math.Max (int.Parse (values [2]), 0)
+    //                                   );
+    //        }
+    //        else
+    //        {
+    //            EnqueueRequestResponseEvent (c1Control, code, values, terminating);
+    //        }
+    //    }
+    //    else
+    //    {
+    //        EnqueueRequestResponseEvent (c1Control, code, values, terminating);
+    //    }
+
+    //    _inputReady.Set ();
+    //}
 
 
-    private void EnqueueRequestResponseEvent (string c1Control, string code, string [] values, string terminating)
-    {
-        var eventType = EventType.RequestResponse;
-        var requestRespEv = new RequestResponseEvent { ResultTuple = (c1Control, code, values, terminating) };
+    //private void EnqueueRequestResponseEvent (string c1Control, string code, string [] values, string terminating)
+    //{
+    //    var eventType = EventType.RequestResponse;
+    //    var requestRespEv = new RequestResponseEvent { ResultTuple = (c1Control, code, values, terminating) };
 
 
-        _inputQueue.Enqueue (
-                             new InputResult { EventType = eventType, RequestResponseEvent = requestRespEv }
-                            );
-    }
+    //    _inputQueue.Enqueue (
+    //                         new InputResult { EventType = eventType, RequestResponseEvent = requestRespEv }
+    //                        );
+    //}
 
 
     private void HandleMouseEvent (MouseButtonState buttonState, Point pos)
     private void HandleMouseEvent (MouseButtonState buttonState, Point pos)
     {
     {
@@ -1042,6 +1052,11 @@ internal class NetDriver : ConsoleDriver
 
 
         StopReportingMouseMoves ();
         StopReportingMouseMoves ();
 
 
+        _ansiResponseTokenSource?.Cancel ();
+        _ansiResponseTokenSource?.Dispose ();
+
+        _waitAnsiResponse?.Dispose ();
+
         if (!RunningUnitTests)
         if (!RunningUnitTests)
         {
         {
             Console.ResetColor ();
             Console.ResetColor ();
@@ -1417,10 +1432,37 @@ internal class NetDriver : ConsoleDriver
         }
         }
     }
     }
 
 
+    internal ManualResetEventSlim _waitAnsiResponse = new (false);
+    private readonly CancellationTokenSource _ansiResponseTokenSource = new ();
+
     /// <inheritdoc />
     /// <inheritdoc />
-    public override bool WriteAnsi (string ansi)
+    public override string WriteAnsi (AnsiEscapeSequenceRequest ansiRequest)
     {
     {
-        return WriteAnsiDefault (ansi);
+        _mainLoopDriver._netEvents.EscSeqRequests.Add (ansiRequest);
+
+        try
+        {
+            if (!_ansiResponseTokenSource.IsCancellationRequested && Console.KeyAvailable)
+            {
+                _mainLoopDriver._netEvents._forceRead = true;
+
+                _mainLoopDriver._netEvents._waitForStart.Set ();
+
+                _waitAnsiResponse.Wait (_ansiResponseTokenSource.Token);
+            }
+        }
+        catch (OperationCanceledException)
+        {
+            return string.Empty;
+        }
+        finally
+        {
+            _waitAnsiResponse.Reset ();
+        }
+
+        _mainLoopDriver._netEvents._forceRead = false;
+
+        return ansiRequest.Response;
     }
     }
 
 
     private MouseEventArgs ToDriverMouse (NetEvents.MouseEvent me)
     private MouseEventArgs ToDriverMouse (NetEvents.MouseEvent me)

+ 10 - 7
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -1185,9 +1185,14 @@ internal class WindowsDriver : ConsoleDriver
     }
     }
 
 
     /// <inheritdoc />
     /// <inheritdoc />
-    public override bool WriteAnsi (string ansi)
+    public override string WriteAnsi (AnsiEscapeSequenceRequest ansiRequest)
     {
     {
-        return WinConsole?.WriteANSI (ansi) ?? false;
+        if (WinConsole?.WriteANSI (ansiRequest.Request) == true)
+        {
+            return ReadAnsiDefault (ansiRequest);
+        }
+
+        return string.Empty;
     }
     }
 
 
     #region Not Implemented
     #region Not Implemented
@@ -1247,7 +1252,7 @@ internal class WindowsDriver : ConsoleDriver
         {
         {
             var sb = new StringBuilder ();
             var sb = new StringBuilder ();
             sb.Append (EscSeqUtils.CSI_SetCursorPosition (position.Y + 1, position.X + 1));
             sb.Append (EscSeqUtils.CSI_SetCursorPosition (position.Y + 1, position.X + 1));
-            WriteAnsi (sb.ToString ());
+            WinConsole?.WriteANSI (sb.ToString ());
         }
         }
 
 
         if (_cachedCursorVisibility is { })
         if (_cachedCursorVisibility is { })
@@ -1283,8 +1288,7 @@ internal class WindowsDriver : ConsoleDriver
         {
         {
             var sb = new StringBuilder ();
             var sb = new StringBuilder ();
             sb.Append (visibility != CursorVisibility.Invisible ? EscSeqUtils.CSI_ShowCursor : EscSeqUtils.CSI_HideCursor);
             sb.Append (visibility != CursorVisibility.Invisible ? EscSeqUtils.CSI_ShowCursor : EscSeqUtils.CSI_HideCursor);
-
-            return WriteAnsi (sb.ToString ());
+            return WinConsole?.WriteANSI (sb.ToString ()) ?? false;
         }
         }
     }
     }
 
 
@@ -1299,8 +1303,7 @@ internal class WindowsDriver : ConsoleDriver
         {
         {
             var sb = new StringBuilder ();
             var sb = new StringBuilder ();
             sb.Append (_cachedCursorVisibility != CursorVisibility.Invisible ? EscSeqUtils.CSI_ShowCursor : EscSeqUtils.CSI_HideCursor);
             sb.Append (_cachedCursorVisibility != CursorVisibility.Invisible ? EscSeqUtils.CSI_ShowCursor : EscSeqUtils.CSI_HideCursor);
-
-            return WriteAnsi (sb.ToString ());
+            return WinConsole?.WriteANSI (sb.ToString ()) ?? false;
         }
         }
 
 
         //if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows))
         //if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows))

+ 8 - 18
UnitTests/Input/EscSeqReqTests.cs

@@ -8,29 +8,21 @@ public class EscSeqReqTests
         var escSeqReq = new EscSeqRequests ();
         var escSeqReq = new EscSeqRequests ();
         escSeqReq.Add (new () { Request = "", Terminator = "t" });
         escSeqReq.Add (new () { Request = "", Terminator = "t" });
         Assert.Single (escSeqReq.Statuses);
         Assert.Single (escSeqReq.Statuses);
-        Assert.Equal ("t", escSeqReq.Statuses [^1].AnsiRequest.Terminator);
-        Assert.Equal (1, escSeqReq.Statuses [^1].NumRequests);
-        Assert.Equal (1, escSeqReq.Statuses [^1].NumOutstanding);
+        Assert.Equal ("t", escSeqReq.Statuses.ToArray () [^1].AnsiRequest.Terminator);
 
 
         escSeqReq.Add (new () { Request = "", Terminator = "t" });
         escSeqReq.Add (new () { Request = "", Terminator = "t" });
-        Assert.Single (escSeqReq.Statuses);
-        Assert.Equal ("t", escSeqReq.Statuses [^1].AnsiRequest.Terminator);
-        Assert.Equal (2, escSeqReq.Statuses [^1].NumRequests);
-        Assert.Equal (2, escSeqReq.Statuses [^1].NumOutstanding);
+        Assert.Equal (2, escSeqReq.Statuses.Count);
+        Assert.Equal ("t", escSeqReq.Statuses.ToArray () [^1].AnsiRequest.Terminator);
 
 
         escSeqReq = new ();
         escSeqReq = new ();
         escSeqReq.Add (new () { Request = "", Terminator = "t" });
         escSeqReq.Add (new () { Request = "", Terminator = "t" });
         escSeqReq.Add (new () { Request = "", Terminator = "t" });
         escSeqReq.Add (new () { Request = "", Terminator = "t" });
-        Assert.Single (escSeqReq.Statuses);
-        Assert.Equal ("t", escSeqReq.Statuses [^1].AnsiRequest.Terminator);
-        Assert.Equal (2, escSeqReq.Statuses [^1].NumRequests);
-        Assert.Equal (2, escSeqReq.Statuses [^1].NumOutstanding);
+        Assert.Equal (2, escSeqReq.Statuses.Count);
+        Assert.Equal ("t", escSeqReq.Statuses.ToArray () [^1].AnsiRequest.Terminator);
 
 
         escSeqReq.Add (new () { Request = "", Terminator = "t" });
         escSeqReq.Add (new () { Request = "", Terminator = "t" });
-        Assert.Single (escSeqReq.Statuses);
-        Assert.Equal ("t", escSeqReq.Statuses [^1].AnsiRequest.Terminator);
-        Assert.Equal (3, escSeqReq.Statuses [^1].NumRequests);
-        Assert.Equal (3, escSeqReq.Statuses [^1].NumOutstanding);
+        Assert.Equal (3, escSeqReq.Statuses.Count);
+        Assert.Equal ("t", escSeqReq.Statuses.ToArray () [^1].AnsiRequest.Terminator);
     }
     }
 
 
     [Fact]
     [Fact]
@@ -55,9 +47,7 @@ public class EscSeqReqTests
         escSeqReq.HasResponse ("t", out seqReqStatus);
         escSeqReq.HasResponse ("t", out seqReqStatus);
         escSeqReq.Remove (seqReqStatus);
         escSeqReq.Remove (seqReqStatus);
         Assert.Single (escSeqReq.Statuses);
         Assert.Single (escSeqReq.Statuses);
-        Assert.Equal ("t", escSeqReq.Statuses [^1].AnsiRequest.Terminator);
-        Assert.Equal (2, escSeqReq.Statuses [^1].NumRequests);
-        Assert.Equal (1, escSeqReq.Statuses [^1].NumOutstanding);
+        Assert.Equal ("t", escSeqReq.Statuses.ToArray () [^1].AnsiRequest.Terminator);
 
 
         escSeqReq.HasResponse ("t", out seqReqStatus);
         escSeqReq.HasResponse ("t", out seqReqStatus);
         escSeqReq.Remove (seqReqStatus);
         escSeqReq.Remove (seqReqStatus);

+ 1 - 1
UnitTests/Input/EscSeqUtilsTests.cs

@@ -841,7 +841,7 @@ public class EscSeqUtilsTests
         };
         };
         expectedCki = default (ConsoleKeyInfo);
         expectedCki = default (ConsoleKeyInfo);
         Assert.Single (_escSeqReqProc.Statuses);
         Assert.Single (_escSeqReqProc.Statuses);
-        Assert.Equal ("t", _escSeqReqProc.Statuses [^1].AnsiRequest.Terminator);
+        Assert.Equal ("t", _escSeqReqProc.Statuses.ToArray () [^1].AnsiRequest.Terminator);
 
 
         EscSeqUtils.DecodeEscSeq (
         EscSeqUtils.DecodeEscSeq (
                                   _escSeqReqProc,
                                   _escSeqReqProc,