Browse Source

Merge pull request #172 from tznind/ansi-parser-net-driver

Ansi parser net driver
Thomas Nind 9 months ago
parent
commit
9aff49179f

+ 6 - 0
Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs

@@ -15,6 +15,12 @@ namespace Terminal.Gui;
 /// </remarks>
 /// </remarks>
 public abstract class ConsoleDriver
 public abstract class ConsoleDriver
 {
 {
+
+    /// <summary>
+    /// How long after Esc has been pressed before we give up on getting an Ansi escape sequence
+    /// </summary>
+    internal TimeSpan EscTimeout = TimeSpan.FromMilliseconds (50);
+
     // As performance is a concern, we keep track of the dirty lines and only refresh those.
     // As performance is a concern, we keep track of the dirty lines and only refresh those.
     // This is in addition to the dirty flag on each cell.
     // This is in addition to the dirty flag on each cell.
     internal bool []? _dirtyLines;
     internal bool []? _dirtyLines;

+ 12 - 0
Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs

@@ -970,6 +970,18 @@ public static class EscSeqUtils
 
 
                 break;
                 break;
             case uint n when n > 0 && n <= KeyEsc:
             case uint n when n > 0 && n <= KeyEsc:
+                if (n == KeyEsc)
+                {
+                    key= ConsoleKey.Escape;
+
+                    newConsoleKeyInfo = new ConsoleKeyInfo (
+                                                            consoleKeyInfo.KeyChar,
+                                                            key,
+                                                            (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
+                                                            (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
+                                                            (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0);
+                }
+                else
                 if (consoleKeyInfo.Key == 0 && consoleKeyInfo.KeyChar == '\r')
                 if (consoleKeyInfo.Key == 0 && consoleKeyInfo.KeyChar == '\r')
                 {
                 {
                     key = ConsoleKey.Enter;
                     key = ConsoleKey.Enter;

+ 100 - 210
Terminal.Gui/ConsoleDrivers/NetDriver.cs

@@ -2,6 +2,7 @@
 // NetDriver.cs: The System.Console-based .NET driver, works on Windows and Unix, but is not particularly efficient.
 // NetDriver.cs: The System.Console-based .NET driver, works on Windows and Unix, but is not particularly efficient.
 //
 //
 
 
+using System.Collections.Concurrent;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
@@ -134,71 +135,60 @@ internal class NetWinVTConsole
 
 
 internal class NetEvents : IDisposable
 internal class NetEvents : IDisposable
 {
 {
-    private readonly ManualResetEventSlim _inputReady = new (false);
-    private CancellationTokenSource _inputReadyCancellationTokenSource;
-    private readonly ManualResetEventSlim _waitForStart = new (false);
+    private readonly CancellationTokenSource _netEventsDisposed = new CancellationTokenSource ();
 
 
     //CancellationTokenSource _waitForStartCancellationTokenSource;
     //CancellationTokenSource _waitForStartCancellationTokenSource;
     private readonly ManualResetEventSlim _winChange = new (false);
     private readonly ManualResetEventSlim _winChange = new (false);
-    private readonly Queue<InputResult?> _inputQueue = new ();
+    private readonly BlockingCollection<InputResult?> _inputQueue = new (new ConcurrentQueue<InputResult?> ());
     private readonly ConsoleDriver _consoleDriver;
     private readonly ConsoleDriver _consoleDriver;
-    private ConsoleKeyInfo [] _cki;
-    private bool _isEscSeq;
-#if PROCESS_REQUEST
-    bool _neededProcessRequest;
-#endif
+
     public EscSeqRequests EscSeqRequests { get; } = new ();
     public EscSeqRequests EscSeqRequests { get; } = new ();
 
 
+    public AnsiResponseParser<ConsoleKeyInfo> Parser { get; private set; } = new ();
+
     public NetEvents (ConsoleDriver consoleDriver)
     public NetEvents (ConsoleDriver consoleDriver)
     {
     {
         _consoleDriver = consoleDriver ?? throw new ArgumentNullException (nameof (consoleDriver));
         _consoleDriver = consoleDriver ?? throw new ArgumentNullException (nameof (consoleDriver));
-        _inputReadyCancellationTokenSource = new CancellationTokenSource ();
 
 
-        Task.Run (ProcessInputQueue, _inputReadyCancellationTokenSource.Token);
+        Task.Run (() =>
+                  {
+                      try
+                      {
+                          ProcessInputQueue ();
+                      }
+                      catch (OperationCanceledException)
+                      { }
+                  }, _netEventsDisposed.Token);
+
+            Task.Run (()=>{
+
+            try
+            {
+                CheckWindowSizeChange();
+            }
+            catch (OperationCanceledException)
+            { }}, _netEventsDisposed.Token);
 
 
-        Task.Run (CheckWindowSizeChange, _inputReadyCancellationTokenSource.Token);
+        Parser.UnexpectedResponseHandler = ProcessRequestResponse;
     }
     }
 
 
+
     public InputResult? DequeueInput ()
     public InputResult? DequeueInput ()
     {
     {
-        while (_inputReadyCancellationTokenSource != null
-               && !_inputReadyCancellationTokenSource.Token.IsCancellationRequested)
+        while (!_netEventsDisposed.Token.IsCancellationRequested)
         {
         {
-            _waitForStart.Set ();
             _winChange.Set ();
             _winChange.Set ();
 
 
-            try
-            {
-                if (!_inputReadyCancellationTokenSource.Token.IsCancellationRequested)
-                {
-                    if (_inputQueue.Count == 0)
-                    {
-                        _inputReady.Wait (_inputReadyCancellationTokenSource.Token);
-                    }
-                }
-            }
-            catch (OperationCanceledException)
-            {
-                return null;
-            }
-            finally
-            {
-                _inputReady.Reset ();
-            }
-
-#if PROCESS_REQUEST
-            _neededProcessRequest = false;
-#endif
-            if (_inputQueue.Count > 0)
+            if (_inputQueue.TryTake (out var item,-1,_netEventsDisposed.Token))
             {
             {
-                return _inputQueue.Dequeue ();
+                return item;
             }
             }
         }
         }
 
 
         return null;
         return null;
     }
     }
 
 
-    private static ConsoleKeyInfo ReadConsoleKeyInfo (CancellationToken cancellationToken, bool intercept = true)
+    private ConsoleKeyInfo ReadConsoleKeyInfo ( bool intercept = true)
     {
     {
         // if there is a key available, return it without waiting
         // if there is a key available, return it without waiting
         //  (or dispatching work to the thread queue)
         //  (or dispatching work to the thread queue)
@@ -207,9 +197,14 @@ internal class NetEvents : IDisposable
             return Console.ReadKey (intercept);
             return Console.ReadKey (intercept);
         }
         }
 
 
-        while (!cancellationToken.IsCancellationRequested)
+        while (!_netEventsDisposed.IsCancellationRequested)
         {
         {
-            Task.Delay (100, cancellationToken).Wait (cancellationToken);
+            Task.Delay (100, _netEventsDisposed.Token).Wait (_netEventsDisposed.Token);
+
+            foreach (var k in ShouldRelease ())
+            {
+                ProcessMapConsoleKeyInfo (k);
+            }
 
 
             if (Console.KeyAvailable)
             if (Console.KeyAvailable)
             {
             {
@@ -217,124 +212,62 @@ internal class NetEvents : IDisposable
             }
             }
         }
         }
 
 
-        cancellationToken.ThrowIfCancellationRequested ();
+        _netEventsDisposed.Token.ThrowIfCancellationRequested ();
 
 
         return default (ConsoleKeyInfo);
         return default (ConsoleKeyInfo);
     }
     }
 
 
-    private void ProcessInputQueue ()
+    public IEnumerable<ConsoleKeyInfo> ShouldRelease ()
     {
     {
-        while (_inputReadyCancellationTokenSource is { IsCancellationRequested: false })
+        if (Parser.State == AnsiResponseParserState.ExpectingBracket &&
+            DateTime.Now - Parser.StateChangedAt > _consoleDriver.EscTimeout)
         {
         {
-            try
-            {
-                _waitForStart.Wait (_inputReadyCancellationTokenSource.Token);
-            }
-            catch (OperationCanceledException)
-            {
-                return;
-            }
+            return Parser.Release ().Select (o => o.Item2);
+        }
 
 
-            _waitForStart.Reset ();
+        return [];
+    }
 
 
+    private void ProcessInputQueue ()
+    {
+        while (!_netEventsDisposed.IsCancellationRequested)
+        {
             if (_inputQueue.Count == 0)
             if (_inputQueue.Count == 0)
             {
             {
-                ConsoleKey key = 0;
-                ConsoleModifiers mod = 0;
-                ConsoleKeyInfo newConsoleKeyInfo = default;
-
-                while (_inputReadyCancellationTokenSource is { IsCancellationRequested: false })
+                while (!_netEventsDisposed.IsCancellationRequested)
                 {
                 {
                     ConsoleKeyInfo consoleKeyInfo;
                     ConsoleKeyInfo consoleKeyInfo;
 
 
-                    try
-                    {
-                        consoleKeyInfo = ReadConsoleKeyInfo (_inputReadyCancellationTokenSource.Token);
-                    }
-                    catch (OperationCanceledException)
-                    {
-                        return;
-                    }
-
-                    if ((consoleKeyInfo.KeyChar == (char)KeyCode.Esc && !_isEscSeq)
-                        || (consoleKeyInfo.KeyChar != (char)KeyCode.Esc && _isEscSeq))
-                    {
-                        if (_cki is null && consoleKeyInfo.KeyChar != (char)KeyCode.Esc && _isEscSeq)
-                        {
-                            _cki = EscSeqUtils.ResizeArray (
-                                                            new ConsoleKeyInfo (
-                                                                                (char)KeyCode.Esc,
-                                                                                0,
-                                                                                false,
-                                                                                false,
-                                                                                false
-                                                                               ),
-                                                            _cki
-                                                           );
-                        }
-
-                        _isEscSeq = true;
-                        newConsoleKeyInfo = consoleKeyInfo;
-                        _cki = EscSeqUtils.ResizeArray (consoleKeyInfo, _cki);
-
-                        if (Console.KeyAvailable)
-                        {
-                            continue;
-                        }
-
-                        ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod);
-                        _cki = null;
-                        _isEscSeq = false;
-
-                        break;
-                    }
+                    consoleKeyInfo = ReadConsoleKeyInfo ();
 
 
-                    if (consoleKeyInfo.KeyChar == (char)KeyCode.Esc && _isEscSeq && _cki is { })
+                    // Parse
+                    foreach (var k in Parser.ProcessInput (Tuple.Create (consoleKeyInfo.KeyChar, consoleKeyInfo)))
                     {
                     {
-                        ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod);
-                        _cki = null;
-
-                        if (Console.KeyAvailable)
-                        {
-                            _cki = EscSeqUtils.ResizeArray (consoleKeyInfo, _cki);
-                        }
-                        else
-                        {
-                            ProcessMapConsoleKeyInfo (consoleKeyInfo);
-                        }
-
-                        break;
+                        ProcessMapConsoleKeyInfo (k.Item2);
                     }
                     }
-
-                    ProcessMapConsoleKeyInfo (consoleKeyInfo);
-
-                    break;
                 }
                 }
             }
             }
-
-            _inputReady.Set ();
         }
         }
+    }
 
 
-        void ProcessMapConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
-        {
-            _inputQueue.Enqueue (
-                                 new InputResult
-                                 {
-                                     EventType = EventType.Key, ConsoleKeyInfo = EscSeqUtils.MapConsoleKeyInfo (consoleKeyInfo)
-                                 }
-                                );
-            _isEscSeq = false;
-        }
+    void ProcessMapConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
+    {
+        _inputQueue.Add (
+                             new InputResult
+                             {
+                                 EventType = EventType.Key, ConsoleKeyInfo = EscSeqUtils.MapConsoleKeyInfo (consoleKeyInfo)
+                             }
+                            );
     }
     }
 
 
     private void CheckWindowSizeChange ()
     private void CheckWindowSizeChange ()
     {
     {
-        void RequestWindowSize (CancellationToken cancellationToken)
+        void RequestWindowSize ()
         {
         {
-            while (!cancellationToken.IsCancellationRequested)
+            while (!_netEventsDisposed.IsCancellationRequested)
             {
             {
                 // Wait for a while then check if screen has changed sizes
                 // Wait for a while then check if screen has changed sizes
-                Task.Delay (500, cancellationToken).Wait (cancellationToken);
+                Task.Delay (500, _netEventsDisposed.Token).Wait (_netEventsDisposed.Token);
 
 
                 int buffHeight, buffWidth;
                 int buffHeight, buffWidth;
 
 
@@ -360,24 +293,22 @@ internal class NetEvents : IDisposable
                 }
                 }
             }
             }
 
 
-            cancellationToken.ThrowIfCancellationRequested ();
+            _netEventsDisposed.Token.ThrowIfCancellationRequested ();
         }
         }
 
 
-        while (_inputReadyCancellationTokenSource is { IsCancellationRequested: false })
+        while (!_netEventsDisposed.IsCancellationRequested)
         {
         {
             try
             try
             {
             {
-                _winChange.Wait (_inputReadyCancellationTokenSource.Token);
+                _winChange.Wait (_netEventsDisposed.Token);
                 _winChange.Reset ();
                 _winChange.Reset ();
 
 
-                RequestWindowSize (_inputReadyCancellationTokenSource.Token);
+                RequestWindowSize ();
             }
             }
             catch (OperationCanceledException)
             catch (OperationCanceledException)
             {
             {
                 return;
                 return;
             }
             }
-
-            _inputReady.Set ();
         }
         }
     }
     }
 
 
@@ -397,7 +328,7 @@ internal class NetEvents : IDisposable
         int w = Math.Max (winWidth, 0);
         int w = Math.Max (winWidth, 0);
         int h = Math.Max (winHeight, 0);
         int h = Math.Max (winHeight, 0);
 
 
-        _inputQueue.Enqueue (
+        _inputQueue.Add (
                              new InputResult
                              new InputResult
                              {
                              {
                                  EventType = EventType.WindowSize, WindowSizeEvent = new WindowSizeEvent { Size = new (w, h) }
                                  EventType = EventType.WindowSize, WindowSizeEvent = new WindowSizeEvent { Size = new (w, h) }
@@ -407,7 +338,18 @@ internal class NetEvents : IDisposable
         return true;
         return true;
     }
     }
 
 
-    public AnsiResponseParser Parser { get; private set; } = new ();
+    private bool ProcessRequestResponse (IEnumerable<Tuple<char, ConsoleKeyInfo>> obj)
+    {
+        // Added for signature compatibility with existing method, not sure what they are even for.
+        ConsoleKeyInfo newConsoleKeyInfo = default;
+        ConsoleKey key = default;
+        ConsoleModifiers mod = default;
+
+        ProcessRequestResponse (ref newConsoleKeyInfo, ref key, obj.Select (v=>v.Item2).ToArray (),ref mod);
+
+        // Handled
+        return true;
+    }
 
 
     // Process a CSI sequence received by the driver (key pressed, mouse event, or request/response event)
     // Process a CSI sequence received by the driver (key pressed, mouse event, or request/response event)
     private void ProcessRequestResponse (
     private void ProcessRequestResponse (
@@ -417,15 +359,6 @@ internal class NetEvents : IDisposable
         ref ConsoleModifiers mod
         ref ConsoleModifiers mod
     )
     )
     {
     {
-        if (cki != null)
-        {
-            // If the response is fully consumed by parser
-            if(cki.Length > 1 && string.IsNullOrEmpty(Parser.ProcessInput (new string(cki.Select (k=>k.KeyChar).ToArray ()))))
-            {
-                // Lets not double process
-                return;
-            }
-        }
 
 
         // isMouse is true if it's CSI<, false otherwise
         // isMouse is true if it's CSI<, false otherwise
         EscSeqUtils.DecodeEscSeq (
         EscSeqUtils.DecodeEscSeq (
@@ -615,7 +548,7 @@ internal class NetEvents : IDisposable
                     var eventType = EventType.WindowPosition;
                     var eventType = EventType.WindowPosition;
                     var winPositionEv = new WindowPositionEvent { CursorPosition = point };
                     var winPositionEv = new WindowPositionEvent { CursorPosition = point };
 
 
-                    _inputQueue.Enqueue (
+                    _inputQueue.Add (
                                          new InputResult { EventType = eventType, WindowPositionEvent = winPositionEv }
                                          new InputResult { EventType = eventType, WindowPositionEvent = winPositionEv }
                                         );
                                         );
                 }
                 }
@@ -650,8 +583,6 @@ internal class NetEvents : IDisposable
 
 
                 break;
                 break;
         }
         }
-
-        _inputReady.Set ();
     }
     }
 
 
     private void EnqueueRequestResponseEvent (string c1Control, string code, string [] values, string terminating)
     private void EnqueueRequestResponseEvent (string c1Control, string code, string [] values, string terminating)
@@ -659,7 +590,7 @@ internal class NetEvents : IDisposable
         var eventType = EventType.RequestResponse;
         var eventType = EventType.RequestResponse;
         var requestRespEv = new RequestResponseEvent { ResultTuple = (c1Control, code, values, terminating) };
         var requestRespEv = new RequestResponseEvent { ResultTuple = (c1Control, code, values, terminating) };
 
 
-        _inputQueue.Enqueue (
+        _inputQueue.Add (
                              new InputResult { EventType = eventType, RequestResponseEvent = requestRespEv }
                              new InputResult { EventType = eventType, RequestResponseEvent = requestRespEv }
                             );
                             );
     }
     }
@@ -668,11 +599,9 @@ internal class NetEvents : IDisposable
     {
     {
         var mouseEvent = new MouseEvent { Position = pos, ButtonState = buttonState };
         var mouseEvent = new MouseEvent { Position = pos, ButtonState = buttonState };
 
 
-        _inputQueue.Enqueue (
+        _inputQueue.Add  (
                              new InputResult { EventType = EventType.Mouse, MouseEvent = mouseEvent }
                              new InputResult { EventType = EventType.Mouse, MouseEvent = mouseEvent }
                             );
                             );
-
-        _inputReady.Set ();
     }
     }
 
 
     public enum EventType
     public enum EventType
@@ -785,14 +714,13 @@ internal class NetEvents : IDisposable
     {
     {
         var inputResult = new InputResult { EventType = EventType.Key, ConsoleKeyInfo = cki };
         var inputResult = new InputResult { EventType = EventType.Key, ConsoleKeyInfo = cki };
 
 
-        _inputQueue.Enqueue (inputResult);
+        _inputQueue.Add (inputResult);
     }
     }
 
 
     public void Dispose ()
     public void Dispose ()
     {
     {
-        _inputReadyCancellationTokenSource?.Cancel ();
-        _inputReadyCancellationTokenSource?.Dispose ();
-        _inputReadyCancellationTokenSource = null;
+        _netEventsDisposed?.Cancel ();
+        _netEventsDisposed?.Dispose ();
 
 
         try
         try
         {
         {
@@ -1710,11 +1638,11 @@ internal class NetMainLoop : IMainLoopDriver
     /// <summary>Invoked when a Key is pressed.</summary>
     /// <summary>Invoked when a Key is pressed.</summary>
     internal Action<InputResult> ProcessInput;
     internal Action<InputResult> ProcessInput;
 
 
-    private readonly ManualResetEventSlim _eventReady = new (false);
     private readonly CancellationTokenSource _inputHandlerTokenSource = new ();
     private readonly CancellationTokenSource _inputHandlerTokenSource = new ();
-    private readonly Queue<InputResult?> _resultQueue = new ();
+    // Wrap ConcurrentQueue in a BlockingCollection to enable blocking with timeout
+    private readonly BlockingCollection<InputResult> _resultQueue = new (new ConcurrentQueue<InputResult> ());
+
     private readonly ManualResetEventSlim _waitForProbe = new (false);
     private readonly ManualResetEventSlim _waitForProbe = new (false);
-    private readonly CancellationTokenSource _eventReadyTokenSource = new ();
     private MainLoop _mainLoop;
     private MainLoop _mainLoop;
 
 
     /// <summary>Initializes the class with the console driver.</summary>
     /// <summary>Initializes the class with the console driver.</summary>
@@ -1737,50 +1665,25 @@ internal class NetMainLoop : IMainLoopDriver
         Task.Run (NetInputHandler, _inputHandlerTokenSource.Token);
         Task.Run (NetInputHandler, _inputHandlerTokenSource.Token);
     }
     }
 
 
-    void IMainLoopDriver.Wakeup () { _eventReady.Set (); }
+    void IMainLoopDriver.Wakeup () {  }
 
 
     bool IMainLoopDriver.EventsPending ()
     bool IMainLoopDriver.EventsPending ()
     {
     {
         _waitForProbe.Set ();
         _waitForProbe.Set ();
 
 
-        if (_mainLoop.CheckTimersAndIdleHandlers (out int waitTimeout))
-        {
-            return true;
-        }
-
-        try
-        {
-            if (!_eventReadyTokenSource.IsCancellationRequested)
-            {
-                // 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);
-            }
-        }
-        catch (OperationCanceledException)
+        if (_mainLoop.CheckTimersAndIdleHandlers (out _))
         {
         {
             return true;
             return true;
         }
         }
-        finally
-        {
-            _eventReady.Reset ();
-        }
-
-        _eventReadyTokenSource.Token.ThrowIfCancellationRequested ();
-
-        if (!_eventReadyTokenSource.IsCancellationRequested)
-        {
-            return _resultQueue.Count > 0 || _mainLoop.CheckTimersAndIdleHandlers (out _);
-        }
 
 
-        return true;
+        return _resultQueue.Count > 0 || _mainLoop.CheckTimersAndIdleHandlers (out _);
     }
     }
 
 
     void IMainLoopDriver.Iteration ()
     void IMainLoopDriver.Iteration ()
     {
     {
-        while (_resultQueue.Count > 0)
+        while (_resultQueue.TryTake (out InputResult v))
         {
         {
-            ProcessInput?.Invoke (_resultQueue.Dequeue ().Value);
+            ProcessInput?.Invoke (v);
         }
         }
     }
     }
 
 
@@ -1788,12 +1691,7 @@ internal class NetMainLoop : IMainLoopDriver
     {
     {
         _inputHandlerTokenSource?.Cancel ();
         _inputHandlerTokenSource?.Cancel ();
         _inputHandlerTokenSource?.Dispose ();
         _inputHandlerTokenSource?.Dispose ();
-        _eventReadyTokenSource?.Cancel ();
-        _eventReadyTokenSource?.Dispose ();
-
-        _eventReady?.Dispose ();
 
 
-        _resultQueue?.Clear ();
         _waitForProbe?.Dispose ();
         _waitForProbe?.Dispose ();
         _netEvents?.Dispose ();
         _netEvents?.Dispose ();
         _netEvents = null;
         _netEvents = null;
@@ -1831,19 +1729,11 @@ internal class NetMainLoop : IMainLoopDriver
 
 
             _inputHandlerTokenSource.Token.ThrowIfCancellationRequested ();
             _inputHandlerTokenSource.Token.ThrowIfCancellationRequested ();
 
 
-            if (_resultQueue.Count == 0)
-            {
-                _resultQueue.Enqueue (_netEvents.DequeueInput ());
-            }
-
-            while (_resultQueue.Count > 0 && _resultQueue.Peek () is null)
-            {
-                _resultQueue.Dequeue ();
-            }
+            var input = _netEvents.DequeueInput ();
 
 
-            if (_resultQueue.Count > 0)
+            if (input.HasValue)
             {
             {
-                _eventReady.Set ();
+                _resultQueue.Add (input.Value);
             }
             }
         }
         }
     }
     }

+ 1 - 6
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -1469,10 +1469,6 @@ internal class WindowsDriver : ConsoleDriver
         return new MainLoop (_mainLoopDriver);
         return new MainLoop (_mainLoopDriver);
     }
     }
 
 
-    /// <summary>
-    /// How long after Esc has been pressed before we give up on getting an Ansi escape sequence
-    /// </summary>
-    private TimeSpan _escTimeout = TimeSpan.FromMilliseconds (50);
     private AnsiResponseParser<WindowsConsole.InputRecord> _parser = new ();
     private AnsiResponseParser<WindowsConsole.InputRecord> _parser = new ();
 
 
     internal void ProcessInput (WindowsConsole.InputRecord inputEvent)
     internal void ProcessInput (WindowsConsole.InputRecord inputEvent)
@@ -1580,9 +1576,8 @@ internal class WindowsDriver : ConsoleDriver
 
 
     public IEnumerable<WindowsConsole.InputRecord> ShouldRelease ()
     public IEnumerable<WindowsConsole.InputRecord> ShouldRelease ()
     {
     {
-
         if (_parser.State == AnsiResponseParserState.ExpectingBracket &&
         if (_parser.State == AnsiResponseParserState.ExpectingBracket &&
-            DateTime.Now - _parser.StateChangedAt > _escTimeout)
+            DateTime.Now - _parser.StateChangedAt > EscTimeout)
         {
         {
             return _parser.Release ().Select (o => o.Item2);
             return _parser.Release ().Select (o => o.Item2);
         }
         }

+ 2 - 1
UICatalog/Properties/launchSettings.json

@@ -1,7 +1,8 @@
 {
 {
   "profiles": {
   "profiles": {
     "UICatalog": {
     "UICatalog": {
-      "commandName": "Project"
+      "commandName": "Project",
+      "commandLineArgs": "-d NetDriver"
     },
     },
     "UICatalog --driver NetDriver": {
     "UICatalog --driver NetDriver": {
       "commandName": "Project",
       "commandName": "Project",