using Microsoft.Extensions.Hosting; namespace LazyBear.MCP.TUI; /// /// Фоновый сервис для глобального чтения клавиш консоли. /// Единственный источник клавишных событий для всего TUI. /// /// Использует выделенный поток с блокирующим Console.ReadKey — никакого /// polling-а, нет обращений к Console.KeyAvailable, которые захватывают /// консольный mutex и мешают рендерингу RazorConsole. /// public sealed class GlobalKeyboardService : IHostedService, IDisposable { public event Action? OnKeyPressed; private Thread? _thread; private volatile bool _stopping; public Task StartAsync(CancellationToken cancellationToken) { if (Console.IsInputRedirected) { return Task.CompletedTask; } _thread = new Thread(ReadLoop) { IsBackground = true, Name = "KeyboardReader" }; _thread.Start(); return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) { _stopping = true; // Console.ReadKey не поддерживает CancellationToken — поток // завершится сам при выходе приложения (IsBackground = true). return Task.CompletedTask; } private void ReadLoop() { while (!_stopping) { try { // Блокирующий вызов: не нагружает CPU и не трогает консольный // mutex в паузах между нажатиями — рендеринг не страдает. var key = Console.ReadKey(intercept: true); if (!_stopping) { OnKeyPressed?.Invoke(key); } } catch (InvalidOperationException) { // stdin стал недоступен (перенаправление и т.п.) break; } catch { Thread.Sleep(100); } } } public void Dispose() { _stopping = true; } }