using System.Collections.Concurrent;
namespace Terminal.Gui.Drivers;
///
/// Base class for reading console input in perpetual loop.
/// The and methods are executed
/// on the input thread created by .
///
///
public abstract class InputImpl : IInput
{
private ConcurrentQueue? _inputQueue;
///
/// Determines how to get the current system type, adjust
/// in unit tests to simulate specific timings.
///
public Func Now { get; set; } = () => DateTime.Now;
///
public CancellationTokenSource? ExternalCancellationTokenSource { get; set; }
///
public void Initialize (ConcurrentQueue inputQueue) { _inputQueue = inputQueue; }
///
public void Run (CancellationToken runCancellationToken)
{
// Create a linked token source if we have an external one
CancellationTokenSource? linkedCts = null;
CancellationToken effectiveToken = runCancellationToken;
if (ExternalCancellationTokenSource != null)
{
linkedCts = CancellationTokenSource.CreateLinkedTokenSource (runCancellationToken, ExternalCancellationTokenSource.Token);
effectiveToken = linkedCts.Token;
}
try
{
if (_inputQueue == null)
{
throw new ("Cannot run input before Initialization");
}
do
{
while (Peek ())
{
foreach (TInputRecord r in Read ())
{
_inputQueue.Enqueue (r);
}
}
effectiveToken.ThrowIfCancellationRequested ();
// Throttle the input loop to avoid CPU spinning when no input is available
// This is especially important when multiple ApplicationImpl instances are created
// in parallel tests without calling Shutdown() - prevents thread pool exhaustion
Task.Delay (20, effectiveToken).Wait (effectiveToken);
}
while (!effectiveToken.IsCancellationRequested);
}
catch (OperationCanceledException)
{ }
finally
{
Logging.Trace ("Stopping input processing");
linkedCts?.Dispose ();
}
}
///
/// When implemented in a derived class, returns true if there is data available
/// to read from console.
///
///
public abstract bool Peek ();
///
/// Returns the available data without blocking, called when
/// returns .
///
///
public abstract IEnumerable Read ();
///
public virtual void Dispose () { }
}