#nullable enable 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 { DateTime dt = Now (); while (Peek ()) { foreach (TInputRecord r in Read ()) { _inputQueue.Enqueue (r); } } effectiveToken.ThrowIfCancellationRequested (); } 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 () { } }