浏览代码

Improves CursesDriver to allow non-blocking input polling.

BDisp 9 月之前
父节点
当前提交
9eb70236d6

+ 29 - 3
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -210,12 +210,38 @@ internal class CursesDriver : ConsoleDriver
     /// <inheritdoc />
     public override string WriteAnsiRequest (AnsiEscapeSequenceRequest ansiRequest)
     {
-        if (WriteAnsiRequestDefault (ansiRequest.Request))
+        if (_mainLoopDriver is null)
         {
-            return ReadAnsiResponseDefault (ansiRequest);
+            return string.Empty;
+        }
+
+        while (Console.KeyAvailable)
+        {
+            _mainLoopDriver._forceRead = true;
+
+            _mainLoopDriver._waitForInput.Set ();
+            _mainLoopDriver._waitForInput.Reset ();
         }
 
-        return string.Empty;
+        _mainLoopDriver._forceRead = false;
+        _mainLoopDriver._suspendRead = true;
+
+        try
+        {
+            Console.Out.Write (ansiRequest.Request);
+            Console.Out.Flush (); // Ensure the request is sent
+            Task.Delay (100).Wait (); // Allow time for the terminal to respond
+
+            return ReadAnsiResponseDefault (ansiRequest);
+        }
+        catch (Exception)
+        {
+            return string.Empty;
+        }
+        finally
+        {
+            _mainLoopDriver._suspendRead = false;
+        }
     }
 
     public override void Suspend ()

+ 98 - 6
Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs

@@ -46,6 +46,10 @@ internal class UnixMainLoop : IMainLoopDriver
     private bool _pollDirty = true;
     private Pollfd [] _pollMap;
     private bool _winChanged;
+    private readonly ManualResetEventSlim _eventReady = new (false);
+    internal readonly ManualResetEventSlim _waitForInput = new (false);
+    private readonly CancellationTokenSource _eventReadyTokenSource = new ();
+    private readonly CancellationTokenSource _inputHandlerTokenSource = new ();
 
     public UnixMainLoop (ConsoleDriver consoleDriver = null)
     {
@@ -89,22 +93,99 @@ internal class UnixMainLoop : IMainLoopDriver
         {
             throw new NotSupportedException ("libc not found", e);
         }
+
+        Task.Run (CursesInputHandler, _inputHandlerTokenSource.Token);
+    }
+
+    internal bool _forceRead;
+    internal bool _suspendRead;
+    private int n;
+
+    private void CursesInputHandler ()
+    {
+        while (_mainLoop is { })
+        {
+            try
+            {
+                UpdatePollMap ();
+
+                if (!_inputHandlerTokenSource.IsCancellationRequested && !_forceRead)
+                {
+                    _waitForInput.Wait (_inputHandlerTokenSource.Token);
+                }
+            }
+            catch (OperationCanceledException)
+            {
+                return;
+            }
+            finally
+            {
+                if (!_inputHandlerTokenSource.IsCancellationRequested)
+                {
+                    _waitForInput.Reset ();
+                }
+            }
+
+            while (!_inputHandlerTokenSource.IsCancellationRequested)
+            {
+                if (!_suspendRead)
+                {
+                    n = poll (_pollMap, (uint)_pollMap.Length, 0);
+
+                    if (n == KEY_RESIZE)
+                    {
+                        _winChanged = true;
+
+                        break;
+                    }
+
+                    if (n > 0)
+                    {
+                        break;
+                    }
+                }
+
+                if (!_forceRead)
+                {
+                    Task.Delay (100, _inputHandlerTokenSource.Token).Wait (_inputHandlerTokenSource.Token);
+                }
+            }
+
+            _eventReady.Set ();
+        }
     }
 
     bool IMainLoopDriver.EventsPending ()
     {
-        UpdatePollMap ();
+        _waitForInput.Set ();
 
-        bool checkTimersResult = _mainLoop.CheckTimersAndIdleHandlers (out int pollTimeout);
+        if (_mainLoop.CheckTimersAndIdleHandlers (out int waitTimeout))
+        {
+            return true;
+        }
 
-        int n = poll (_pollMap, (uint)_pollMap.Length, pollTimeout);
+        try
+        {
+            if (!_eventReadyTokenSource.IsCancellationRequested)
+            {
+                _eventReady.Wait (waitTimeout, _eventReadyTokenSource.Token);
+            }
+        }
+        catch (OperationCanceledException)
+        {
+            return true;
+        }
+        finally
+        {
+            _eventReady.Reset ();
+        }
 
-        if (n == KEY_RESIZE)
+        if (!_eventReadyTokenSource.IsCancellationRequested)
         {
-            _winChanged = true;
+            return n > 0 || _mainLoop.CheckTimersAndIdleHandlers (out _) || _winChanged;
         }
 
-        return checkTimersResult || n >= KEY_RESIZE;
+        return true;
     }
 
     void IMainLoopDriver.Iteration ()
@@ -118,6 +199,8 @@ internal class UnixMainLoop : IMainLoopDriver
             _cursesDriver.ProcessWinChange ();
         }
 
+        n = 0;
+
         if (_pollMap is null)
         {
             return;
@@ -148,6 +231,15 @@ internal class UnixMainLoop : IMainLoopDriver
     {
         _descriptorWatchers?.Clear ();
 
+        _inputHandlerTokenSource?.Cancel ();
+        _inputHandlerTokenSource?.Dispose ();
+
+        _waitForInput?.Dispose ();
+
+        _eventReadyTokenSource?.Cancel ();
+        _eventReadyTokenSource?.Dispose ();
+        _eventReady?.Dispose ();
+
         _mainLoop = null;
     }