mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-31 02:08:03 +01:00
Change to BlockingCollection, thanks to @tznind.
This commit is contained in:
@@ -754,17 +754,7 @@ internal class NetDriver : ConsoleDriver
|
||||
_mainLoopDriver._netEvents._forceRead = true;
|
||||
}
|
||||
|
||||
if (!_ansiResponseTokenSource.IsCancellationRequested)
|
||||
{
|
||||
_mainLoopDriver._netEvents._waitForStart.Set ();
|
||||
|
||||
if (!_mainLoopDriver._waitForProbe.IsSet)
|
||||
{
|
||||
_mainLoopDriver._waitForProbe.Set ();
|
||||
}
|
||||
|
||||
_waitAnsiResponse.Wait (_ansiResponseTokenSource.Token);
|
||||
}
|
||||
_waitAnsiResponse.Wait (_ansiResponseTokenSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
|
||||
@@ -6,13 +6,8 @@ namespace Terminal.Gui;
|
||||
|
||||
internal class NetEvents : IDisposable
|
||||
{
|
||||
private readonly ManualResetEventSlim _inputReady = new (false);
|
||||
private CancellationTokenSource _inputReadyCancellationTokenSource;
|
||||
internal readonly ManualResetEventSlim _waitForStart = new (false);
|
||||
|
||||
//CancellationTokenSource _waitForStartCancellationTokenSource;
|
||||
private readonly ManualResetEventSlim _winChange = new (false);
|
||||
private readonly ConcurrentQueue<InputResult?> _inputQueue = new ();
|
||||
private readonly BlockingCollection<InputResult> _inputQueue = new (new ConcurrentQueue<InputResult> ());
|
||||
private readonly ConsoleDriver _consoleDriver;
|
||||
private ConsoleKeyInfo [] _cki;
|
||||
private bool _isEscSeq;
|
||||
@@ -33,41 +28,20 @@ internal class NetEvents : IDisposable
|
||||
|
||||
public InputResult? DequeueInput ()
|
||||
{
|
||||
while (_inputReadyCancellationTokenSource != null
|
||||
&& !_inputReadyCancellationTokenSource.Token.IsCancellationRequested)
|
||||
while (_inputReadyCancellationTokenSource is { })
|
||||
{
|
||||
_waitForStart.Set ();
|
||||
_winChange.Set ();
|
||||
|
||||
try
|
||||
{
|
||||
if (!_inputReadyCancellationTokenSource.Token.IsCancellationRequested)
|
||||
{
|
||||
if (_inputQueue.Count == 0)
|
||||
{
|
||||
_inputReady.Wait (_inputReadyCancellationTokenSource.Token);
|
||||
}
|
||||
}
|
||||
return _inputQueue.Take (_inputReadyCancellationTokenSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_inputReady.Reset ();
|
||||
}
|
||||
|
||||
#if PROCESS_REQUEST
|
||||
_neededProcessRequest = false;
|
||||
#endif
|
||||
if (_inputQueue.Count > 0)
|
||||
{
|
||||
if (_inputQueue.TryDequeue (out InputResult? result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -130,129 +104,120 @@ internal class NetEvents : IDisposable
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_forceRead)
|
||||
if (_inputQueue.Count == 0 || _forceRead)
|
||||
{
|
||||
_waitForStart.Wait (_inputReadyCancellationTokenSource.Token);
|
||||
ConsoleKey key = 0;
|
||||
ConsoleModifiers mod = 0;
|
||||
ConsoleKeyInfo newConsoleKeyInfo = default;
|
||||
|
||||
while (_inputReadyCancellationTokenSource is { IsCancellationRequested: false })
|
||||
{
|
||||
ConsoleKeyInfo consoleKeyInfo;
|
||||
|
||||
try
|
||||
{
|
||||
consoleKeyInfo = ReadConsoleKeyInfo (_inputReadyCancellationTokenSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AnsiEscapeSequenceRequestUtils.IncompleteCkInfos is { })
|
||||
{
|
||||
AnsiEscapeSequenceRequestUtils.InsertArray (AnsiEscapeSequenceRequestUtils.IncompleteCkInfos, _cki);
|
||||
}
|
||||
|
||||
if ((consoleKeyInfo.KeyChar == (char)KeyCode.Esc && !_isEscSeq)
|
||||
|| (consoleKeyInfo.KeyChar != (char)KeyCode.Esc && _isEscSeq))
|
||||
{
|
||||
if (_cki is null && consoleKeyInfo.KeyChar != (char)KeyCode.Esc && _isEscSeq)
|
||||
{
|
||||
_cki = AnsiEscapeSequenceRequestUtils.ResizeArray (
|
||||
new (
|
||||
(char)KeyCode.Esc,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
),
|
||||
_cki
|
||||
);
|
||||
}
|
||||
|
||||
_isEscSeq = true;
|
||||
|
||||
if ((_cki is { } && _cki [^1].KeyChar != Key.Esc && consoleKeyInfo.KeyChar != Key.Esc && consoleKeyInfo.KeyChar <= Key.Space)
|
||||
|| (_cki is { } && _cki [^1].KeyChar != '\u001B' && consoleKeyInfo.KeyChar == 127)
|
||||
|| (_cki is { } && char.IsLetter (_cki [^1].KeyChar) && char.IsLower (consoleKeyInfo.KeyChar) && char.IsLetter (consoleKeyInfo.KeyChar))
|
||||
|| (_cki is { Length: > 2 } && char.IsLetter (_cki [^1].KeyChar) && char.IsLetter (consoleKeyInfo.KeyChar)))
|
||||
{
|
||||
ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod);
|
||||
_cki = null;
|
||||
_isEscSeq = false;
|
||||
|
||||
ProcessMapConsoleKeyInfo (consoleKeyInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
newConsoleKeyInfo = consoleKeyInfo;
|
||||
_cki = AnsiEscapeSequenceRequestUtils.ResizeArray (consoleKeyInfo, _cki);
|
||||
|
||||
if (Console.KeyAvailable)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod);
|
||||
_cki = null;
|
||||
_isEscSeq = false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (consoleKeyInfo.KeyChar == (char)KeyCode.Esc && _isEscSeq && _cki is { })
|
||||
{
|
||||
ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod);
|
||||
_cki = null;
|
||||
|
||||
if (Console.KeyAvailable)
|
||||
{
|
||||
_cki = AnsiEscapeSequenceRequestUtils.ResizeArray (consoleKeyInfo, _cki);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessMapConsoleKeyInfo (consoleKeyInfo);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ProcessMapConsoleKeyInfo (consoleKeyInfo);
|
||||
|
||||
if (_retries > 0)
|
||||
{
|
||||
_retries = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_waitForStart.Reset ();
|
||||
|
||||
if (_inputQueue.Count == 0 || _forceRead)
|
||||
{
|
||||
ConsoleKey key = 0;
|
||||
ConsoleModifiers mod = 0;
|
||||
ConsoleKeyInfo newConsoleKeyInfo = default;
|
||||
|
||||
while (_inputReadyCancellationTokenSource is { IsCancellationRequested: false })
|
||||
{
|
||||
ConsoleKeyInfo consoleKeyInfo;
|
||||
|
||||
try
|
||||
{
|
||||
consoleKeyInfo = ReadConsoleKeyInfo (_inputReadyCancellationTokenSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AnsiEscapeSequenceRequestUtils.IncompleteCkInfos is { })
|
||||
{
|
||||
AnsiEscapeSequenceRequestUtils.InsertArray (AnsiEscapeSequenceRequestUtils.IncompleteCkInfos, _cki);
|
||||
}
|
||||
|
||||
if ((consoleKeyInfo.KeyChar == (char)KeyCode.Esc && !_isEscSeq)
|
||||
|| (consoleKeyInfo.KeyChar != (char)KeyCode.Esc && _isEscSeq))
|
||||
{
|
||||
if (_cki is null && consoleKeyInfo.KeyChar != (char)KeyCode.Esc && _isEscSeq)
|
||||
{
|
||||
_cki = AnsiEscapeSequenceRequestUtils.ResizeArray (
|
||||
new ConsoleKeyInfo (
|
||||
(char)KeyCode.Esc,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
),
|
||||
_cki
|
||||
);
|
||||
}
|
||||
|
||||
_isEscSeq = true;
|
||||
|
||||
if ((_cki is { } && _cki [^1].KeyChar != Key.Esc && consoleKeyInfo.KeyChar != Key.Esc && consoleKeyInfo.KeyChar <= Key.Space)
|
||||
|| (_cki is { } && _cki [^1].KeyChar != '\u001B' && consoleKeyInfo.KeyChar == 127)
|
||||
|| (_cki is { } && char.IsLetter (_cki [^1].KeyChar) && char.IsLower (consoleKeyInfo.KeyChar) && char.IsLetter (consoleKeyInfo.KeyChar))
|
||||
|| (_cki is { Length: > 2 } && char.IsLetter (_cki [^1].KeyChar) && char.IsLetter (consoleKeyInfo.KeyChar)))
|
||||
{
|
||||
ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod);
|
||||
_cki = null;
|
||||
_isEscSeq = false;
|
||||
|
||||
ProcessMapConsoleKeyInfo (consoleKeyInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
newConsoleKeyInfo = consoleKeyInfo;
|
||||
_cki = AnsiEscapeSequenceRequestUtils.ResizeArray (consoleKeyInfo, _cki);
|
||||
|
||||
if (Console.KeyAvailable)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod);
|
||||
_cki = null;
|
||||
_isEscSeq = false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (consoleKeyInfo.KeyChar == (char)KeyCode.Esc && _isEscSeq && _cki is { })
|
||||
{
|
||||
ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod);
|
||||
_cki = null;
|
||||
|
||||
if (Console.KeyAvailable)
|
||||
{
|
||||
_cki = AnsiEscapeSequenceRequestUtils.ResizeArray (consoleKeyInfo, _cki);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessMapConsoleKeyInfo (consoleKeyInfo);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ProcessMapConsoleKeyInfo (consoleKeyInfo);
|
||||
|
||||
if (_retries > 0)
|
||||
{
|
||||
_retries = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_inputReady.Set ();
|
||||
}
|
||||
|
||||
void ProcessMapConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
|
||||
{
|
||||
_inputQueue.Enqueue (
|
||||
new InputResult
|
||||
{
|
||||
EventType = EventType.Key, ConsoleKeyInfo = AnsiEscapeSequenceRequestUtils.MapConsoleKeyInfo (consoleKeyInfo)
|
||||
}
|
||||
);
|
||||
_inputQueue.Add (
|
||||
new ()
|
||||
{
|
||||
EventType = EventType.Key, ConsoleKeyInfo = AnsiEscapeSequenceRequestUtils.MapConsoleKeyInfo (consoleKeyInfo)
|
||||
}
|
||||
);
|
||||
_isEscSeq = false;
|
||||
}
|
||||
}
|
||||
@@ -297,17 +262,12 @@ internal class NetEvents : IDisposable
|
||||
{
|
||||
try
|
||||
{
|
||||
_winChange.Wait (_inputReadyCancellationTokenSource.Token);
|
||||
_winChange.Reset ();
|
||||
|
||||
RequestWindowSize (_inputReadyCancellationTokenSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_inputReady.Set ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,12 +287,12 @@ internal class NetEvents : IDisposable
|
||||
int w = Math.Max (winWidth, 0);
|
||||
int h = Math.Max (winHeight, 0);
|
||||
|
||||
_inputQueue.Enqueue (
|
||||
new InputResult
|
||||
{
|
||||
EventType = EventType.WindowSize, WindowSizeEvent = new WindowSizeEvent { Size = new (w, h) }
|
||||
}
|
||||
);
|
||||
_inputQueue.Add (
|
||||
new ()
|
||||
{
|
||||
EventType = EventType.WindowSize, WindowSizeEvent = new () { Size = new (w, h) }
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -609,11 +569,9 @@ internal class NetEvents : IDisposable
|
||||
{
|
||||
var mouseEvent = new MouseEvent { Position = pos, ButtonState = buttonState };
|
||||
|
||||
_inputQueue.Enqueue (
|
||||
new InputResult { EventType = EventType.Mouse, MouseEvent = mouseEvent }
|
||||
);
|
||||
|
||||
_inputReady.Set ();
|
||||
_inputQueue.Add (
|
||||
new () { EventType = EventType.Mouse, MouseEvent = mouseEvent }
|
||||
);
|
||||
}
|
||||
|
||||
public enum EventType
|
||||
@@ -726,7 +684,7 @@ internal class NetEvents : IDisposable
|
||||
{
|
||||
var inputResult = new InputResult { EventType = EventType.Key, ConsoleKeyInfo = cki };
|
||||
|
||||
_inputQueue.Enqueue (inputResult);
|
||||
_inputQueue.Add (inputResult);
|
||||
}
|
||||
|
||||
public void Dispose ()
|
||||
|
||||
@@ -16,8 +16,7 @@ internal class NetMainLoop : IMainLoopDriver
|
||||
|
||||
private readonly ManualResetEventSlim _eventReady = new (false);
|
||||
private readonly CancellationTokenSource _inputHandlerTokenSource = new ();
|
||||
private readonly ConcurrentQueue<NetEvents.InputResult?> _resultQueue = new ();
|
||||
internal readonly ManualResetEventSlim _waitForProbe = new (false);
|
||||
private readonly BlockingCollection<NetEvents.InputResult> _resultQueue = new (new ConcurrentQueue<NetEvents.InputResult> ());
|
||||
private readonly CancellationTokenSource _eventReadyTokenSource = new ();
|
||||
private MainLoop _mainLoop;
|
||||
|
||||
@@ -27,12 +26,9 @@ internal class NetMainLoop : IMainLoopDriver
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public NetMainLoop (ConsoleDriver consoleDriver = null)
|
||||
{
|
||||
if (consoleDriver is null)
|
||||
{
|
||||
throw new ArgumentNullException (nameof (consoleDriver));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull (consoleDriver);
|
||||
|
||||
_netEvents = new NetEvents (consoleDriver);
|
||||
_netEvents = new (consoleDriver);
|
||||
}
|
||||
|
||||
void IMainLoopDriver.Setup (MainLoop mainLoop)
|
||||
@@ -51,9 +47,7 @@ internal class NetMainLoop : IMainLoopDriver
|
||||
|
||||
bool IMainLoopDriver.EventsPending ()
|
||||
{
|
||||
_waitForProbe.Set ();
|
||||
|
||||
if (_mainLoop.CheckTimersAndIdleHandlers (out int waitTimeout))
|
||||
if (_resultQueue.Count > 0 || _mainLoop.CheckTimersAndIdleHandlers (out int waitTimeout))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -83,6 +77,7 @@ internal class NetMainLoop : IMainLoopDriver
|
||||
return _resultQueue.Count > 0 || _mainLoop.CheckTimersAndIdleHandlers (out _);
|
||||
}
|
||||
|
||||
// If cancellation was requested then always return true
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -91,11 +86,11 @@ internal class NetMainLoop : IMainLoopDriver
|
||||
while (_resultQueue.Count > 0)
|
||||
{
|
||||
// Always dequeue even if it's null and invoke if isn't null
|
||||
if (_resultQueue.TryDequeue (out NetEvents.InputResult? dequeueResult))
|
||||
if (_resultQueue.TryTake (out NetEvents.InputResult dequeueResult))
|
||||
{
|
||||
if (dequeueResult is { })
|
||||
{
|
||||
ProcessInput?.Invoke (dequeueResult.Value);
|
||||
ProcessInput?.Invoke (dequeueResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,8 +105,7 @@ internal class NetMainLoop : IMainLoopDriver
|
||||
|
||||
_eventReady?.Dispose ();
|
||||
|
||||
_resultQueue?.Clear ();
|
||||
_waitForProbe?.Dispose ();
|
||||
_resultQueue?.Dispose();
|
||||
_netEvents?.Dispose ();
|
||||
_netEvents = null;
|
||||
|
||||
@@ -124,50 +118,30 @@ internal class NetMainLoop : IMainLoopDriver
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_netEvents._forceRead && !_inputHandlerTokenSource.IsCancellationRequested)
|
||||
if (_inputHandlerTokenSource.IsCancellationRequested)
|
||||
{
|
||||
_waitForProbe.Wait (_inputHandlerTokenSource.Token);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_resultQueue?.Count == 0 || _netEvents._forceRead)
|
||||
{
|
||||
NetEvents.InputResult? result = _netEvents.DequeueInput ();
|
||||
|
||||
if (result.HasValue)
|
||||
{
|
||||
_resultQueue?.Add (result.Value);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_inputHandlerTokenSource.IsCancellationRequested && _resultQueue?.Count > 0)
|
||||
{
|
||||
_eventReady.Set ();
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_waitForProbe.IsSet)
|
||||
{
|
||||
_waitForProbe.Reset ();
|
||||
}
|
||||
}
|
||||
|
||||
if (_inputHandlerTokenSource.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_inputHandlerTokenSource.Token.ThrowIfCancellationRequested ();
|
||||
|
||||
if (_resultQueue.Count == 0)
|
||||
{
|
||||
_resultQueue.Enqueue (_netEvents.DequeueInput ());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
while (_resultQueue.Count > 0 && _resultQueue.TryPeek (out NetEvents.InputResult? result) && result is null)
|
||||
{
|
||||
// Dequeue null values
|
||||
_resultQueue.TryDequeue (out _);
|
||||
}
|
||||
}
|
||||
catch (InvalidOperationException) // Peek can raise an exception
|
||||
{ }
|
||||
|
||||
if (_resultQueue.Count > 0)
|
||||
{
|
||||
_eventReady.Set ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user