You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
181 lines
5.3 KiB
181 lines
5.3 KiB
4 weeks ago
|
using System.Security.Principal;
|
||
|
using Mindmagma.Curses;
|
||
|
|
||
|
namespace ANSI_Fahrplan;
|
||
|
|
||
|
public class InputHandler {
|
||
|
/// <summary>
|
||
|
/// Handler called by the input handler routine when a key is pressed
|
||
|
/// </summary>
|
||
|
/// <param name="sender">Sender object of event callback</param>
|
||
|
/// <param name="e">Event arguments with details like the pressed key code</param>
|
||
|
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;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Class constructor
|
||
|
/// </summary>
|
||
|
/// <param name="targetWindow">Window for which to listen to key presses</param>
|
||
|
public InputHandler (nint targetWindow) {
|
||
|
_targetWindow = targetWindow;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Start listening to key presses and call registered callbacks
|
||
|
/// <paramref name="window">Window to listen to key presses in<param/>
|
||
|
/// </summary>
|
||
|
public void StartListening () {
|
||
|
if (IsListening ()) return;
|
||
|
|
||
|
listeningRoutineCts = new CancellationTokenSource ();
|
||
|
|
||
|
listeningTask = Task.Run (_ListeningRoutine);
|
||
|
while (listeningTask.Status == TaskStatus.WaitingToRun) Thread.Sleep (50);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Stop listening to key presses
|
||
|
/// </summary>
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns the current listening status of the input event handler
|
||
|
/// </summary>
|
||
|
/// <returns>True if the event handler routine is listening to key presses</returns>
|
||
|
public bool IsListening () {
|
||
|
return listeningTask is not null && (
|
||
|
listeningTask.Status == TaskStatus.Running ||
|
||
|
listeningTask.Status == TaskStatus.WaitingForActivation
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Enable temporary raw keypress event handler to forward
|
||
|
/// all keypressed directly to a specific function
|
||
|
/// </summary>
|
||
|
/// <param name="eventHandler">Event handler to pass key presses to</param>
|
||
|
/// <param name="screen">Screen to pass key char duty to</param>
|
||
|
public void EnableRawEventHandler (KeypressEventHandler eventHandler, nint? window = null) {
|
||
|
if (window is not null) {
|
||
|
StopListening ();
|
||
|
_targetWindow = (nint) window;
|
||
|
StartListening ();
|
||
|
}
|
||
|
|
||
|
_rawKeyPressHandler = eventHandler;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Disables the temporary raw keypress event handler
|
||
|
/// <param name="screen">Screen to pass key char duty back to</param>
|
||
|
/// </summary>
|
||
|
public void DisableRawEventHandler (nint? screen = null) {
|
||
|
if (RawKeyPressHandler is null) return;
|
||
|
|
||
|
if (screen is not null) {
|
||
|
StopListening ();
|
||
|
_targetWindow = (nint) screen;
|
||
|
StartListening ();
|
||
|
}
|
||
|
|
||
|
_rawKeyPressHandler = null;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Prepares the window for proper key press reading
|
||
|
/// </summary>
|
||
|
private void WindowSetup () {
|
||
|
NCurses.Keypad (TargetWindow, true);
|
||
|
NCurses.NoDelay (TargetWindow, false);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Actual keypress handler being called as Task by StartListening ()
|
||
|
/// </summary>
|
||
|
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;
|
||
|
}
|
||
|
}
|