using System.Security.Principal; using Mindmagma.Curses; namespace ANSI_Fahrplan; public class InputHandler { /// /// Handler called by the input handler routine when a key is pressed /// /// Sender object of event callback /// Event arguments with details like the pressed key code public delegate void KeypressEventHandler (object sender, NCursesKeyPressEventArgs e); // Event fired when key is pressed public event KeypressEventHandler? OnKeyPress; // Similar to OnKeyPress, but called before any regular OnKeyPress event handlers are called public event KeypressEventHandler? OnKeyPressPrivileged; // When this variable is set, no event handlers will be called except this one // Once e.CancelNextEvent is true from this event, it will be set to null again private KeypressEventHandler? _rawKeyPressHandler; public KeypressEventHandler? RawKeyPressHandler { get { return _rawKeyPressHandler; }} private nint _targetWindow; public nint TargetWindow { get { return _targetWindow; }} private CancellationTokenSource? listeningRoutineCts; private Task? listeningTask; /// /// Class constructor /// /// Window for which to listen to key presses public InputHandler (nint targetWindow) { _targetWindow = targetWindow; } /// /// Start listening to key presses and call registered callbacks /// Window to listen to key presses in /// public void StartListening () { if (IsListening ()) return; listeningRoutineCts = new CancellationTokenSource (); listeningTask = Task.Run (_ListeningRoutine); while (listeningTask.Status == TaskStatus.WaitingToRun) Thread.Sleep (50); } /// /// Stop listening to key presses /// public void StopListening () { if (! IsListening ()) return; if (listeningRoutineCts is null) return; if (listeningTask is null) return; listeningRoutineCts.Cancel (); listeningTask.Wait (); listeningTask = null; NCurses.Keypad (TargetWindow, false); } /// /// Returns the current listening status of the input event handler /// /// True if the event handler routine is listening to key presses public bool IsListening () { return listeningTask is not null && ( listeningTask.Status == TaskStatus.Running || listeningTask.Status == TaskStatus.WaitingForActivation ); } /// /// Enable temporary raw keypress event handler to forward /// all keypressed directly to a specific function /// /// Event handler to pass key presses to /// Screen to pass key char duty to public void EnableRawEventHandler (KeypressEventHandler eventHandler, nint? window = null) { if (window is not null) { StopListening (); _targetWindow = (nint) window; StartListening (); } _rawKeyPressHandler = eventHandler; } /// /// Disables the temporary raw keypress event handler /// Screen to pass key char duty back to /// public void DisableRawEventHandler (nint? screen = null) { if (RawKeyPressHandler is null) return; if (screen is not null) { StopListening (); _targetWindow = (nint) screen; StartListening (); } _rawKeyPressHandler = null; } /// /// Prepares the window for proper key press reading /// private void WindowSetup () { NCurses.Keypad (TargetWindow, true); NCurses.NoDelay (TargetWindow, false); } /// /// Actual keypress handler being called as Task by StartListening () /// private void _ListeningRoutine () { if (listeningRoutineCts is null) return; WindowSetup (); while (!listeningRoutineCts.Token.IsCancellationRequested) { int keyCode = NCurses.WindowGetChar (TargetWindow); // Do nothing until the key code is larger than -1 if (keyCode < 0) continue; var eventArgs = new NCursesKeyPressEventArgs (keyCode, TargetWindow); // If enabled, forward all key pressed directly to raw keypress event handler if (RawKeyPressHandler is not null) { RawKeyPressHandler (this, eventArgs); continue; } var skipRegularEventHandlers = false; // Handle any registered privileged key handlers if (OnKeyPressPrivileged is not null) { foreach (KeypressEventHandler h in OnKeyPressPrivileged.GetInvocationList ()) { h (this, eventArgs); if (eventArgs.CancelNextEvent == true) { skipRegularEventHandlers = true; break; } } } // Handle any regular registered key handlers if (OnKeyPress is not null && !skipRegularEventHandlers) { foreach (KeypressEventHandler h in OnKeyPress.GetInvocationList ()) { h (this, eventArgs); if (eventArgs.CancelNextEvent == true) continue ; } } } listeningRoutineCts.Dispose (); listeningRoutineCts = null; } } public class NCursesKeyPressEventArgs : EventArgs { private int _keyCode; public int KeyCode { get { return _keyCode; }} private nint _targetWindow; public nint TargetWindow { get { return _targetWindow; }} public bool CancelNextEvent { get; set; } = false; public NCursesKeyPressEventArgs (int keyCode, nint targetWindow) { _keyCode = keyCode; _targetWindow = targetWindow; } }