#nullable enable using System.Collections.Concurrent; namespace Terminal.Gui.Drivers; /// /// Interface for reading console input in a perpetual loop on a dedicated input thread. /// /// /// /// Implementations run on a separate thread (started by /// ) /// and continuously read platform-specific input from the console, placing it into a thread-safe queue /// for processing by on the main UI thread. /// /// /// Architecture: /// /// /// Input Thread: Main UI Thread: /// ┌─────────────────┐ ┌──────────────────────┐ /// │ IInput.Run() │ │ IInputProcessor │ /// │ ├─ Peek() │ │ ├─ ProcessQueue() │ /// │ ├─ Read() │──Enqueue──→ │ ├─ Process() │ /// │ └─ Enqueue │ │ ├─ ToKey() │ /// └─────────────────┘ │ └─ Raise Events │ /// └──────────────────────┘ /// /// /// Lifecycle: /// /// /// - Set the shared input queue /// - Start the perpetual read loop (blocks until cancelled) /// /// Loop calls and /// /// Cancellation via `runCancellationToken` or /// /// /// Implementations: /// /// /// - Uses Windows Console API (ReadConsoleInput) /// - Uses .NET API /// - Uses Unix terminal APIs /// - For testing, implements /// /// /// Testing Support: See for programmatic input injection /// in test scenarios. /// /// /// /// The platform-specific input record type: /// /// - for .NET and Fake drivers /// - for Windows driver /// - for Unix driver /// /// public interface IInput : IDisposable { /// /// Gets or sets an external cancellation token source that can stop the loop /// in addition to the `runCancellationToken` passed to . /// /// /// /// This property allows external code (e.g., test harnesses like GuiTestContext) to /// provide additional cancellation signals such as timeouts or hard-stop conditions. /// /// /// Ownership: The setter does NOT transfer ownership of the . /// The creator is responsible for disposal. implementations /// should NOT dispose this token source. /// /// /// How it works: creates a linked token that /// responds to BOTH the `runCancellationToken` AND this external token: /// /// /// var linkedToken = CancellationTokenSource.CreateLinkedTokenSource( /// runCancellationToken, /// ExternalCancellationTokenSource.Token); /// /// /// /// Test scenario with timeout: /// /// var input = new FakeInput(); /// input.ExternalCancellationTokenSource = new CancellationTokenSource( /// TimeSpan.FromSeconds(30)); // 30-second timeout /// /// // Run will stop if either: /// // 1. runCancellationToken is cancelled (normal shutdown) /// // 2. 30 seconds elapse (timeout) /// input.Run(normalCancellationToken); /// /// CancellationTokenSource? ExternalCancellationTokenSource { get; set; } /// /// Initializes the input reader with the thread-safe queue where read input will be stored. /// /// /// The shared that both (producer) /// and (consumer) use for passing input records between threads. /// /// /// /// This queue is created by /// and shared between the input thread and main UI thread. /// /// /// Must be called before . Calling without /// initialization will throw an exception. /// /// void Initialize (ConcurrentQueue inputQueue); /// /// Runs the input loop, continuously reading input and placing it into the queue /// provided by . /// /// /// The primary cancellation token that stops the input loop. Provided by /// and triggered /// during application shutdown. /// /// /// /// Threading: This method runs on a dedicated input thread created by /// . and blocks until /// cancellation is requested. It should never be called from the main UI thread. /// /// /// Cancellation: The loop stops when either /// or (if set) is cancelled. /// /// /// Base Implementation: provides the /// standard loop logic: /// /// /// while (!cancelled) /// { /// while (Peek()) // Check for available input /// { /// foreach (var input in Read()) // Read all available /// { /// inputQueue.Enqueue(input); // Store for processing /// } /// } /// Task.Delay(20ms); // Throttle to ~50 polls/second /// } /// /// /// Testing: For implementations, /// test input injected via /// flows through the same Peek/Read pipeline. /// /// /// /// Thrown when or /// is cancelled. This is the normal/expected means of exiting the input loop. /// /// /// Thrown if was not called before . /// void Run (CancellationToken runCancellationToken); }