فهرست منبع

Make scheduler part of main loop and hide implementation details from public API

tznind 9 ماه پیش
والد
کامیت
5052024061

+ 7 - 0
Terminal.Gui/Application/MainLoop.cs

@@ -270,6 +270,8 @@ internal class MainLoop : IDisposable
             }
         }
 
+        RunAnsiScheduler ();
+
         MainLoopDriver.Iteration ();
 
         var runIdle = false;
@@ -285,6 +287,11 @@ internal class MainLoop : IDisposable
         }
     }
 
+    private void RunAnsiScheduler ()
+    {
+        Application.Driver?.GetRequestScheduler ().RunSchedule ();
+    }
+
     /// <summary>Stops the main loop driver and calls <see cref="IMainLoopDriver.Wakeup"/>. Used only for unit tests.</summary>
     internal void Stop ()
     {

+ 10 - 1
Terminal.Gui/ConsoleDrivers/AnsiResponseParser.cs

@@ -13,6 +13,7 @@ public class AnsiRequestScheduler(IAnsiResponseParser parser)
     private ConcurrentDictionary<string, DateTime> _lastSend = new ();
 
     private TimeSpan _throttle = TimeSpan.FromMilliseconds (100);
+    private TimeSpan _runScheduleThrottle = TimeSpan.FromMilliseconds (100);
 
     /// <summary>
     /// Sends the <paramref name="request"/> immediately or queues it if there is already
@@ -35,15 +36,23 @@ public class AnsiRequestScheduler(IAnsiResponseParser parser)
         }
     }
 
+    private DateTime _lastRun = DateTime.Now;
 
     /// <summary>
     /// Identifies and runs any <see cref="Requests"/> that can be sent based on the
     /// current outstanding requests of the parser.
     /// </summary>
+    /// <param name="force">Repeated requests to run the schedule over short period of time will be ignored.
+    /// Pass <see langword="true"/> to override this behaviour and force evaluation of outstanding requests.</param>
     /// <returns><see langword="true"/> if a request was found and run. <see langword="false"/>
     /// if no outstanding requests or all have existing outstanding requests underway in parser.</returns>
-    public bool RunSchedule ()
+    public bool RunSchedule (bool force = false)
     {
+        if (!force && DateTime.Now - _lastRun < _runScheduleThrottle)
+        {
+            return false;
+        }
+
         var opportunity = Requests.FirstOrDefault (CanSend);
 
         if (opportunity != null)

+ 25 - 5
Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs

@@ -13,7 +13,7 @@ namespace Terminal.Gui;
 ///     <see cref="WindowsDriver"/> - <see cref="NetDriver"/> that uses the .NET Console API - <see cref="FakeConsole"/>
 ///     for unit testing.
 /// </remarks>
-public abstract class ConsoleDriver : IConsoleDriver
+public abstract class ConsoleDriver
 {
     // 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.
@@ -610,11 +610,31 @@ public abstract class ConsoleDriver : IConsoleDriver
 
     #endregion
 
-    /// <inheritdoc />
-    public abstract IAnsiResponseParser GetParser ();
+    private AnsiRequestScheduler? _scheduler;
 
-    /// <inheritdoc />
-    public abstract void RawWrite (string str);
+    /// <summary>
+    /// Queues the given <paramref name="request"/> for execution
+    /// </summary>
+    /// <param name="request"></param>
+    public void QueueAnsiRequest (AnsiEscapeSequenceRequest request)
+    {
+        GetRequestScheduler ().SendOrSchedule (request);
+    }
+
+    protected abstract IAnsiResponseParser GetParser ();
+
+    internal AnsiRequestScheduler GetRequestScheduler ()
+    {
+        // Lazy initialization because GetParser is virtual
+        return _scheduler ??= new (GetParser ());
+    }
+
+    /// <summary>
+    /// Writes the given <paramref name="str"/> directly to the console (rather than the output
+    /// draw buffer).
+    /// </summary>
+    /// <param name="str"></param>
+    internal abstract void RawWrite (string str);
 }
 
 /// <summary>

+ 4 - 4
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -194,7 +194,7 @@ internal class CursesDriver : ConsoleDriver
     }
 
     /// <inheritdoc />
-    public override void RawWrite (string str)
+    internal override void RawWrite (string str)
     {
         Console.Out.Write (str);
     }
@@ -583,9 +583,9 @@ internal class CursesDriver : ConsoleDriver
         return new MainLoop (_mainLoopDriver);
     }
 
-    private AnsiResponseParser Parser { get; set; } = new ();
+    private readonly AnsiResponseParser _parser = new ();
     /// <inheritdoc />
-    public override IAnsiResponseParser GetParser () => Parser;
+    protected override IAnsiResponseParser GetParser () => _parser;
 
     internal void ProcessInput ()
     {
@@ -889,7 +889,7 @@ internal class CursesDriver : ConsoleDriver
                     {
                         foreach (var c in cki)
                         {
-                            Parser.ProcessInput (c.KeyChar.ToString());
+                            _parser.ProcessInput (c.KeyChar.ToString());
                         }
                     }
 

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

@@ -395,10 +395,10 @@ public class FakeDriver : ConsoleDriver
     private AnsiResponseParser _parser = new ();
 
     /// <inheritdoc />
-    public override IAnsiResponseParser GetParser () => _parser;
+    protected override IAnsiResponseParser GetParser () => _parser;
 
     /// <inheritdoc />
-    public override void RawWrite (string str) { throw new NotImplementedException (); }
+    internal override void RawWrite (string str) { }
 
     public void SetBufferSize (int width, int height)
     {

+ 0 - 8
Terminal.Gui/ConsoleDrivers/IConsoleDriver.cs

@@ -1,8 +0,0 @@
-#nullable enable
-namespace Terminal.Gui;
-
-public interface IConsoleDriver
-{
-    IAnsiResponseParser GetParser ();
-    void RawWrite (string str);
-}

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

@@ -1049,10 +1049,10 @@ internal class NetDriver : ConsoleDriver
     }
 
     /// <inheritdoc />
-    public override IAnsiResponseParser GetParser () => _mainLoopDriver._netEvents.Parser;
+    protected override IAnsiResponseParser GetParser () => _mainLoopDriver._netEvents.Parser;
 
     /// <inheritdoc />
-    public override void RawWrite (string str)
+    internal override void RawWrite (string str)
     {
         Console.Write (str);
     }

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

@@ -1165,10 +1165,10 @@ internal class WindowsDriver : ConsoleDriver
     }
 
     /// <inheritdoc />
-    public override IAnsiResponseParser GetParser () => Parser;
+    protected override IAnsiResponseParser GetParser () => _parser;
 
     /// <inheritdoc />
-    public override void RawWrite (string str) => WinConsole?.WriteANSI (str);
+    internal override void RawWrite (string str) => WinConsole?.WriteANSI (str);
 
     #region Not Implemented
 
@@ -1458,7 +1458,7 @@ internal class WindowsDriver : ConsoleDriver
     /// How long after Esc has been pressed before we give up on getting an Ansi escape sequence
     /// </summary>
     private TimeSpan _escTimeout = TimeSpan.FromMilliseconds (50);
-    public AnsiResponseParser<WindowsConsole.InputRecord> Parser { get; set; } = new ();
+    private AnsiResponseParser<WindowsConsole.InputRecord> _parser = new ();
 
     internal void ProcessInput (WindowsConsole.InputRecord inputEvent)
     {
@@ -1557,7 +1557,7 @@ internal class WindowsDriver : ConsoleDriver
         }
 
         foreach (Tuple<char, WindowsConsole.InputRecord> output in
-                 Parser.ProcessInput (Tuple.Create (inputEvent.KeyEvent.UnicodeChar, inputEvent)))
+                 _parser.ProcessInput (Tuple.Create (inputEvent.KeyEvent.UnicodeChar, inputEvent)))
         {
                 yield return output.Item2;
         }
@@ -1566,10 +1566,10 @@ internal class WindowsDriver : ConsoleDriver
     public IEnumerable<WindowsConsole.InputRecord> ShouldRelease ()
     {
 
-        if (Parser.State == AnsiResponseParserState.ExpectingBracket &&
-            DateTime.Now - Parser.StateChangedAt > _escTimeout)
+        if (_parser.State == AnsiResponseParserState.ExpectingBracket &&
+            DateTime.Now - _parser.StateChangedAt > _escTimeout)
         {
-            return Parser.Release ().Select (o => o.Item2);
+            return _parser.Release ().Select (o => o.Item2);
         }
 
         return [];

+ 7 - 14
UICatalog/Scenarios/AnsiRequestsScenario.cs

@@ -24,14 +24,11 @@ public class AnsiRequestsScenario : Scenario
     private List<DateTime> sends = new  ();
     private Dictionary<DateTime,string> answers = new ();
     private Label _lblSummary;
-    private AnsiRequestScheduler _scheduler;
 
     public override void Main ()
     {
         Application.Init ();
 
-        _scheduler = new AnsiRequestScheduler (Application.Driver.GetParser ());
-        
         _win = new Window { Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}" };
 
         var lbl = new Label ()
@@ -102,10 +99,6 @@ public class AnsiRequestsScenario : Scenario
                                                 lastSendTime = currentTime; // Update the last send time
                                             }
                                         }
-
-                                        // TODO: Scheduler probably should be part of core driver
-                                        // Also any that we didn't get a chance to send
-                                        _scheduler.RunSchedule();
                                     }
 
                                     return true;
@@ -204,13 +197,13 @@ public class AnsiRequestsScenario : Scenario
 
     private void SendDar ()
     {
-        _scheduler.SendOrSchedule (
-                        new ()
-                        {
-                            Request = EscSeqUtils.CSI_SendDeviceAttributes,
-                            Terminator = EscSeqUtils.CSI_ReportDeviceAttributes_Terminator,
-                            ResponseReceived = HandleResponse
-                        });
+        Application.Driver.QueueAnsiRequest (
+                                             new ()
+                                             {
+                                                 Request = EscSeqUtils.CSI_SendDeviceAttributes,
+                                                 Terminator = EscSeqUtils.CSI_ReportDeviceAttributes_Terminator,
+                                                 ResponseReceived = HandleResponse
+                                             });
         sends.Add (DateTime.Now);
     }