|
@@ -13,1638 +13,1361 @@ using System.Threading;
|
|
|
using System.Threading.Tasks;
|
|
using System.Threading.Tasks;
|
|
|
using System.Text;
|
|
using System.Text;
|
|
|
|
|
|
|
|
-namespace Terminal.Gui {
|
|
|
|
|
- internal class NetWinVTConsole {
|
|
|
|
|
- IntPtr InputHandle, OutputHandle, ErrorHandle;
|
|
|
|
|
- uint originalInputConsoleMode, originalOutputConsoleMode, originalErrorConsoleMode;
|
|
|
|
|
|
|
+namespace Terminal.Gui;
|
|
|
|
|
+internal class NetWinVTConsole {
|
|
|
|
|
+ IntPtr _inputHandle, _outputHandle, _errorHandle;
|
|
|
|
|
+ uint _originalInputConsoleMode, _originalOutputConsoleMode, _originalErrorConsoleMode;
|
|
|
|
|
|
|
|
- public NetWinVTConsole ()
|
|
|
|
|
- {
|
|
|
|
|
- InputHandle = GetStdHandle (STD_INPUT_HANDLE);
|
|
|
|
|
- if (!GetConsoleMode (InputHandle, out uint mode)) {
|
|
|
|
|
- throw new ApplicationException ($"Failed to get input console mode, error code: {GetLastError ()}.");
|
|
|
|
|
- }
|
|
|
|
|
- originalInputConsoleMode = mode;
|
|
|
|
|
- if ((mode & ENABLE_VIRTUAL_TERMINAL_INPUT) < ENABLE_VIRTUAL_TERMINAL_INPUT) {
|
|
|
|
|
- mode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
|
|
|
|
|
- if (!SetConsoleMode (InputHandle, mode)) {
|
|
|
|
|
- throw new ApplicationException ($"Failed to set input console mode, error code: {GetLastError ()}.");
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- OutputHandle = GetStdHandle (STD_OUTPUT_HANDLE);
|
|
|
|
|
- if (!GetConsoleMode (OutputHandle, out mode)) {
|
|
|
|
|
- throw new ApplicationException ($"Failed to get output console mode, error code: {GetLastError ()}.");
|
|
|
|
|
- }
|
|
|
|
|
- originalOutputConsoleMode = mode;
|
|
|
|
|
- if ((mode & (ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) {
|
|
|
|
|
- mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
|
|
|
|
|
- if (!SetConsoleMode (OutputHandle, mode)) {
|
|
|
|
|
- throw new ApplicationException ($"Failed to set output console mode, error code: {GetLastError ()}.");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ public NetWinVTConsole ()
|
|
|
|
|
+ {
|
|
|
|
|
+ _inputHandle = GetStdHandle (STD_INPUT_HANDLE);
|
|
|
|
|
+ if (!GetConsoleMode (_inputHandle, out uint mode)) {
|
|
|
|
|
+ throw new ApplicationException ($"Failed to get input console mode, error code: {GetLastError ()}.");
|
|
|
|
|
+ }
|
|
|
|
|
+ _originalInputConsoleMode = mode;
|
|
|
|
|
+ if ((mode & ENABLE_VIRTUAL_TERMINAL_INPUT) < ENABLE_VIRTUAL_TERMINAL_INPUT) {
|
|
|
|
|
+ mode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
|
|
|
|
|
+ if (!SetConsoleMode (_inputHandle, mode)) {
|
|
|
|
|
+ throw new ApplicationException ($"Failed to set input console mode, error code: {GetLastError ()}.");
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- ErrorHandle = GetStdHandle (STD_ERROR_HANDLE);
|
|
|
|
|
- if (!GetConsoleMode (ErrorHandle, out mode)) {
|
|
|
|
|
- throw new ApplicationException ($"Failed to get error console mode, error code: {GetLastError ()}.");
|
|
|
|
|
- }
|
|
|
|
|
- originalErrorConsoleMode = mode;
|
|
|
|
|
- if ((mode & (DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) {
|
|
|
|
|
- mode |= DISABLE_NEWLINE_AUTO_RETURN;
|
|
|
|
|
- if (!SetConsoleMode (ErrorHandle, mode)) {
|
|
|
|
|
- throw new ApplicationException ($"Failed to set error console mode, error code: {GetLastError ()}.");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ _outputHandle = GetStdHandle (STD_OUTPUT_HANDLE);
|
|
|
|
|
+ if (!GetConsoleMode (_outputHandle, out mode)) {
|
|
|
|
|
+ throw new ApplicationException ($"Failed to get output console mode, error code: {GetLastError ()}.");
|
|
|
|
|
+ }
|
|
|
|
|
+ _originalOutputConsoleMode = mode;
|
|
|
|
|
+ if ((mode & (ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) {
|
|
|
|
|
+ mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
|
|
|
|
|
+ if (!SetConsoleMode (_outputHandle, mode)) {
|
|
|
|
|
+ throw new ApplicationException ($"Failed to set output console mode, error code: {GetLastError ()}.");
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public void Cleanup ()
|
|
|
|
|
- {
|
|
|
|
|
- if (!SetConsoleMode (InputHandle, originalInputConsoleMode)) {
|
|
|
|
|
- throw new ApplicationException ($"Failed to restore input console mode, error code: {GetLastError ()}.");
|
|
|
|
|
- }
|
|
|
|
|
- if (!SetConsoleMode (OutputHandle, originalOutputConsoleMode)) {
|
|
|
|
|
- throw new ApplicationException ($"Failed to restore output console mode, error code: {GetLastError ()}.");
|
|
|
|
|
- }
|
|
|
|
|
- if (!SetConsoleMode (ErrorHandle, originalErrorConsoleMode)) {
|
|
|
|
|
- throw new ApplicationException ($"Failed to restore error console mode, error code: {GetLastError ()}.");
|
|
|
|
|
|
|
+ _errorHandle = GetStdHandle (STD_ERROR_HANDLE);
|
|
|
|
|
+ if (!GetConsoleMode (_errorHandle, out mode)) {
|
|
|
|
|
+ throw new ApplicationException ($"Failed to get error console mode, error code: {GetLastError ()}.");
|
|
|
|
|
+ }
|
|
|
|
|
+ _originalErrorConsoleMode = mode;
|
|
|
|
|
+ if ((mode & (DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) {
|
|
|
|
|
+ mode |= DISABLE_NEWLINE_AUTO_RETURN;
|
|
|
|
|
+ if (!SetConsoleMode (_errorHandle, mode)) {
|
|
|
|
|
+ throw new ApplicationException ($"Failed to set error console mode, error code: {GetLastError ()}.");
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- const int STD_INPUT_HANDLE = -10;
|
|
|
|
|
- const int STD_OUTPUT_HANDLE = -11;
|
|
|
|
|
- const int STD_ERROR_HANDLE = -12;
|
|
|
|
|
-
|
|
|
|
|
- // Input modes.
|
|
|
|
|
- const uint ENABLE_PROCESSED_INPUT = 1;
|
|
|
|
|
- const uint ENABLE_LINE_INPUT = 2;
|
|
|
|
|
- const uint ENABLE_ECHO_INPUT = 4;
|
|
|
|
|
- const uint ENABLE_WINDOW_INPUT = 8;
|
|
|
|
|
- const uint ENABLE_MOUSE_INPUT = 16;
|
|
|
|
|
- const uint ENABLE_INSERT_MODE = 32;
|
|
|
|
|
- const uint ENABLE_QUICK_EDIT_MODE = 64;
|
|
|
|
|
- const uint ENABLE_EXTENDED_FLAGS = 128;
|
|
|
|
|
- const uint ENABLE_VIRTUAL_TERMINAL_INPUT = 512;
|
|
|
|
|
-
|
|
|
|
|
- // Output modes.
|
|
|
|
|
- const uint ENABLE_PROCESSED_OUTPUT = 1;
|
|
|
|
|
- const uint ENABLE_WRAP_AT_EOL_OUTPUT = 2;
|
|
|
|
|
- const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4;
|
|
|
|
|
- const uint DISABLE_NEWLINE_AUTO_RETURN = 8;
|
|
|
|
|
- const uint ENABLE_LVB_GRID_WORLDWIDE = 10;
|
|
|
|
|
-
|
|
|
|
|
- [DllImport ("kernel32.dll", SetLastError = true)]
|
|
|
|
|
- static extern IntPtr GetStdHandle (int nStdHandle);
|
|
|
|
|
-
|
|
|
|
|
- [DllImport ("kernel32.dll")]
|
|
|
|
|
- static extern bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode);
|
|
|
|
|
-
|
|
|
|
|
- [DllImport ("kernel32.dll")]
|
|
|
|
|
- static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode);
|
|
|
|
|
-
|
|
|
|
|
- [DllImport ("kernel32.dll")]
|
|
|
|
|
- static extern uint GetLastError ();
|
|
|
|
|
|
|
+ public void Cleanup ()
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!SetConsoleMode (_inputHandle, _originalInputConsoleMode)) {
|
|
|
|
|
+ throw new ApplicationException ($"Failed to restore input console mode, error code: {GetLastError ()}.");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!SetConsoleMode (_outputHandle, _originalOutputConsoleMode)) {
|
|
|
|
|
+ throw new ApplicationException ($"Failed to restore output console mode, error code: {GetLastError ()}.");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!SetConsoleMode (_errorHandle, _originalErrorConsoleMode)) {
|
|
|
|
|
+ throw new ApplicationException ($"Failed to restore error console mode, error code: {GetLastError ()}.");
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- internal class NetEvents {
|
|
|
|
|
- ManualResetEventSlim inputReady = new ManualResetEventSlim (false);
|
|
|
|
|
- ManualResetEventSlim waitForStart = new ManualResetEventSlim (false);
|
|
|
|
|
- ManualResetEventSlim winChange = new ManualResetEventSlim (false);
|
|
|
|
|
- Queue<InputResult?> inputResultQueue = new Queue<InputResult?> ();
|
|
|
|
|
- ConsoleDriver consoleDriver;
|
|
|
|
|
- volatile ConsoleKeyInfo [] cki = null;
|
|
|
|
|
- static volatile bool isEscSeq;
|
|
|
|
|
- int lastWindowHeight;
|
|
|
|
|
- bool stopTasks;
|
|
|
|
|
|
|
+ const int STD_INPUT_HANDLE = -10;
|
|
|
|
|
+ const int STD_OUTPUT_HANDLE = -11;
|
|
|
|
|
+ const int STD_ERROR_HANDLE = -12;
|
|
|
|
|
+
|
|
|
|
|
+ // Input modes.
|
|
|
|
|
+ const uint ENABLE_PROCESSED_INPUT = 1;
|
|
|
|
|
+ const uint ENABLE_LINE_INPUT = 2;
|
|
|
|
|
+ const uint ENABLE_ECHO_INPUT = 4;
|
|
|
|
|
+ const uint ENABLE_WINDOW_INPUT = 8;
|
|
|
|
|
+ const uint ENABLE_MOUSE_INPUT = 16;
|
|
|
|
|
+ const uint ENABLE_INSERT_MODE = 32;
|
|
|
|
|
+ const uint ENABLE_QUICK_EDIT_MODE = 64;
|
|
|
|
|
+ const uint ENABLE_EXTENDED_FLAGS = 128;
|
|
|
|
|
+ const uint ENABLE_VIRTUAL_TERMINAL_INPUT = 512;
|
|
|
|
|
+
|
|
|
|
|
+ // Output modes.
|
|
|
|
|
+ const uint ENABLE_PROCESSED_OUTPUT = 1;
|
|
|
|
|
+ const uint ENABLE_WRAP_AT_EOL_OUTPUT = 2;
|
|
|
|
|
+ const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4;
|
|
|
|
|
+ const uint DISABLE_NEWLINE_AUTO_RETURN = 8;
|
|
|
|
|
+ const uint ENABLE_LVB_GRID_WORLDWIDE = 10;
|
|
|
|
|
+
|
|
|
|
|
+ [DllImport ("kernel32.dll", SetLastError = true)]
|
|
|
|
|
+ static extern IntPtr GetStdHandle (int nStdHandle);
|
|
|
|
|
+
|
|
|
|
|
+ [DllImport ("kernel32.dll")]
|
|
|
|
|
+ static extern bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode);
|
|
|
|
|
+
|
|
|
|
|
+ [DllImport ("kernel32.dll")]
|
|
|
|
|
+ static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode);
|
|
|
|
|
+
|
|
|
|
|
+ [DllImport ("kernel32.dll")]
|
|
|
|
|
+ static extern uint GetLastError ();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+internal class NetEvents {
|
|
|
|
|
+ ManualResetEventSlim _inputReady = new ManualResetEventSlim (false);
|
|
|
|
|
+ ManualResetEventSlim _waitForStart = new ManualResetEventSlim (false);
|
|
|
|
|
+ ManualResetEventSlim _winChange = new ManualResetEventSlim (false);
|
|
|
|
|
+ Queue<InputResult?> _inputResultQueue = new Queue<InputResult?> ();
|
|
|
|
|
+ ConsoleDriver _consoleDriver;
|
|
|
|
|
+ volatile ConsoleKeyInfo [] _cki = null;
|
|
|
|
|
+ volatile static bool _isEscSeq;
|
|
|
|
|
+ bool _stopTasks;
|
|
|
#if PROCESS_REQUEST
|
|
#if PROCESS_REQUEST
|
|
|
- bool neededProcessRequest;
|
|
|
|
|
|
|
+ bool _neededProcessRequest;
|
|
|
#endif
|
|
#endif
|
|
|
- public bool IsTerminalWithOptions { get; set; }
|
|
|
|
|
- public EscSeqReqProc EscSeqReqProc { get; } = new EscSeqReqProc ();
|
|
|
|
|
|
|
+ public EscSeqRequests EscSeqRequests { get; } = new EscSeqRequests ();
|
|
|
|
|
|
|
|
- public NetEvents (ConsoleDriver consoleDriver)
|
|
|
|
|
- {
|
|
|
|
|
- if (consoleDriver == null) {
|
|
|
|
|
- throw new ArgumentNullException ("Console driver instance must be provided.");
|
|
|
|
|
- }
|
|
|
|
|
- this.consoleDriver = consoleDriver;
|
|
|
|
|
- Task.Run (ProcessInputResultQueue);
|
|
|
|
|
- Task.Run (CheckWinChange);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ public NetEvents (ConsoleDriver consoleDriver)
|
|
|
|
|
+ {
|
|
|
|
|
+ _consoleDriver = consoleDriver ?? throw new ArgumentNullException (nameof (consoleDriver));
|
|
|
|
|
+ Task.Run (ProcessInputResultQueue);
|
|
|
|
|
+ Task.Run (CheckWindowSizeChange);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- internal void StopTasks ()
|
|
|
|
|
- {
|
|
|
|
|
- stopTasks = true;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ internal void StopTasks ()
|
|
|
|
|
+ {
|
|
|
|
|
+ _stopTasks = true;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- public InputResult? ReadConsoleInput ()
|
|
|
|
|
- {
|
|
|
|
|
- while (true) {
|
|
|
|
|
- if (stopTasks) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- waitForStart.Set ();
|
|
|
|
|
- winChange.Set ();
|
|
|
|
|
|
|
+ public InputResult? ReadConsoleInput ()
|
|
|
|
|
+ {
|
|
|
|
|
+ while (true) {
|
|
|
|
|
+ if (_stopTasks) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ _waitForStart.Set ();
|
|
|
|
|
+ _winChange.Set ();
|
|
|
|
|
|
|
|
- if (inputResultQueue.Count == 0) {
|
|
|
|
|
- inputReady.Wait ();
|
|
|
|
|
- inputReady.Reset ();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (_inputResultQueue.Count == 0) {
|
|
|
|
|
+ _inputReady.Wait ();
|
|
|
|
|
+ _inputReady.Reset ();
|
|
|
|
|
+ }
|
|
|
#if PROCESS_REQUEST
|
|
#if PROCESS_REQUEST
|
|
|
- neededProcessRequest = false;
|
|
|
|
|
|
|
+ _neededProcessRequest = false;
|
|
|
#endif
|
|
#endif
|
|
|
- if (inputResultQueue.Count > 0) {
|
|
|
|
|
- return inputResultQueue.Dequeue ();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- void ProcessInputResultQueue ()
|
|
|
|
|
- {
|
|
|
|
|
- while (true) {
|
|
|
|
|
- waitForStart.Wait ();
|
|
|
|
|
- waitForStart.Reset ();
|
|
|
|
|
-
|
|
|
|
|
- if (inputResultQueue.Count == 0) {
|
|
|
|
|
- GetConsoleKey ();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- inputReady.Set ();
|
|
|
|
|
|
|
+ if (_inputResultQueue.Count > 0) {
|
|
|
|
|
+ return _inputResultQueue.Dequeue ();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- void GetConsoleKey ()
|
|
|
|
|
- {
|
|
|
|
|
- ConsoleKey key = 0;
|
|
|
|
|
- ConsoleModifiers mod = 0;
|
|
|
|
|
- ConsoleKeyInfo newConsoleKeyInfo = default;
|
|
|
|
|
-
|
|
|
|
|
- while (true) {
|
|
|
|
|
- ConsoleKeyInfo consoleKeyInfo = Console.ReadKey (true);
|
|
|
|
|
- if ((consoleKeyInfo.KeyChar == (char)Key.Esc && !isEscSeq)
|
|
|
|
|
- || (consoleKeyInfo.KeyChar != (char)Key.Esc && isEscSeq)) {
|
|
|
|
|
- if (cki == null && consoleKeyInfo.KeyChar != (char)Key.Esc && isEscSeq) {
|
|
|
|
|
- cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)Key.Esc, 0,
|
|
|
|
|
- false, false, false), cki);
|
|
|
|
|
- }
|
|
|
|
|
- isEscSeq = true;
|
|
|
|
|
- newConsoleKeyInfo = consoleKeyInfo;
|
|
|
|
|
- cki = EscSeqUtils.ResizeArray (consoleKeyInfo, cki);
|
|
|
|
|
- if (!Console.KeyAvailable) {
|
|
|
|
|
- DecodeEscSeq (ref newConsoleKeyInfo, ref key, cki, ref mod);
|
|
|
|
|
- cki = null;
|
|
|
|
|
- isEscSeq = false;
|
|
|
|
|
|
|
+ void ProcessInputResultQueue ()
|
|
|
|
|
+ {
|
|
|
|
|
+ while (true) {
|
|
|
|
|
+ _waitForStart.Wait ();
|
|
|
|
|
+ _waitForStart.Reset ();
|
|
|
|
|
+
|
|
|
|
|
+ if (_inputResultQueue.Count == 0) {
|
|
|
|
|
+ ConsoleKey key = 0;
|
|
|
|
|
+ ConsoleModifiers mod = 0;
|
|
|
|
|
+ ConsoleKeyInfo newConsoleKeyInfo = default;
|
|
|
|
|
+
|
|
|
|
|
+ while (true) {
|
|
|
|
|
+ ConsoleKeyInfo consoleKeyInfo = Console.ReadKey (true);
|
|
|
|
|
+ if ((consoleKeyInfo.KeyChar == (char)Key.Esc && !_isEscSeq)
|
|
|
|
|
+ || (consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq)) {
|
|
|
|
|
+ if (_cki == null && consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq) {
|
|
|
|
|
+ _cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)Key.Esc, 0,
|
|
|
|
|
+ false, false, false), _cki);
|
|
|
|
|
+ }
|
|
|
|
|
+ _isEscSeq = true;
|
|
|
|
|
+ newConsoleKeyInfo = consoleKeyInfo;
|
|
|
|
|
+ _cki = EscSeqUtils.ResizeArray (consoleKeyInfo, _cki);
|
|
|
|
|
+ if (Console.KeyAvailable) continue;
|
|
|
|
|
+ ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod);
|
|
|
|
|
+ _cki = null;
|
|
|
|
|
+ _isEscSeq = false;
|
|
|
|
|
+ break;
|
|
|
|
|
+ } else if (consoleKeyInfo.KeyChar == (char)Key.Esc && _isEscSeq && _cki != null) {
|
|
|
|
|
+ if (_cki != null) {
|
|
|
|
|
+ ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod);
|
|
|
|
|
+ _cki = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ _inputResultQueue.Enqueue (new InputResult {
|
|
|
|
|
+ EventType = EventType.Key,
|
|
|
|
|
+ ConsoleKeyInfo = EscSeqUtils.MapConsoleKeyInfo (consoleKeyInfo)
|
|
|
|
|
+ });
|
|
|
|
|
+ _isEscSeq = false;
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
- } else if (consoleKeyInfo.KeyChar == (char)Key.Esc && isEscSeq && cki != null) {
|
|
|
|
|
- DecodeEscSeq (ref newConsoleKeyInfo, ref key, cki, ref mod);
|
|
|
|
|
- cki = null;
|
|
|
|
|
- break;
|
|
|
|
|
- } else {
|
|
|
|
|
- GetConsoleInputType (consoleKeyInfo);
|
|
|
|
|
- isEscSeq = false;
|
|
|
|
|
- break;
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- void CheckWinChange ()
|
|
|
|
|
- {
|
|
|
|
|
- while (true) {
|
|
|
|
|
- if (stopTasks) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- winChange.Wait ();
|
|
|
|
|
- winChange.Reset ();
|
|
|
|
|
- WaitWinChange ();
|
|
|
|
|
- inputReady.Set ();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ _inputReady.Set ();
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- void WaitWinChange ()
|
|
|
|
|
|
|
+ void CheckWindowSizeChange ()
|
|
|
|
|
+ {
|
|
|
|
|
+ void RequestWindowSize ()
|
|
|
{
|
|
{
|
|
|
while (true) {
|
|
while (true) {
|
|
|
- // HACK: Sleep for 10ms to mitigate high CPU usage (see issue #1502). 10ms was tested to address the problem, but may not be correct.
|
|
|
|
|
- Thread.Sleep (10);
|
|
|
|
|
- if (stopTasks) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- switch (IsTerminalWithOptions) {
|
|
|
|
|
- case false:
|
|
|
|
|
- int buffHeight, buffWidth;
|
|
|
|
|
- if (((NetDriver)consoleDriver).IsWinPlatform) {
|
|
|
|
|
- buffHeight = Math.Max (Console.BufferHeight, 0);
|
|
|
|
|
- buffWidth = Math.Max (Console.BufferWidth, 0);
|
|
|
|
|
- } else {
|
|
|
|
|
- buffHeight = consoleDriver.Rows;
|
|
|
|
|
- buffWidth = consoleDriver.Cols;
|
|
|
|
|
- }
|
|
|
|
|
- if (IsWinChanged (
|
|
|
|
|
- Math.Max (Console.WindowHeight, 0),
|
|
|
|
|
- Math.Max (Console.WindowWidth, 0),
|
|
|
|
|
- buffHeight,
|
|
|
|
|
- buffWidth)) {
|
|
|
|
|
|
|
+ // Wait for a while then check if screen has changed sizes
|
|
|
|
|
+ Task.Delay (500).Wait ();
|
|
|
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- break;
|
|
|
|
|
- case true:
|
|
|
|
|
- //Request the size of the text area in characters.
|
|
|
|
|
- EscSeqReqProc.Add ("t");
|
|
|
|
|
- Console.Out.Write ("\x1b[18t");
|
|
|
|
|
- break;
|
|
|
|
|
|
|
+ if (_stopTasks) {
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- bool IsWinChanged (int winHeight, int winWidth, int buffHeight, int buffWidth)
|
|
|
|
|
- {
|
|
|
|
|
- if (!consoleDriver.EnableConsoleScrolling) {
|
|
|
|
|
- if (winWidth != consoleDriver.Cols || winHeight != consoleDriver.Rows) {
|
|
|
|
|
- var w = Math.Max (winWidth, 0);
|
|
|
|
|
- var h = Math.Max (winHeight, 0);
|
|
|
|
|
- GetWindowSizeEvent (new Size (w, h));
|
|
|
|
|
- return true;
|
|
|
|
|
|
|
+ int buffHeight, buffWidth;
|
|
|
|
|
+ if (((NetDriver)_consoleDriver).IsWinPlatform) {
|
|
|
|
|
+ buffHeight = Math.Max (Console.BufferHeight, 0);
|
|
|
|
|
+ buffWidth = Math.Max (Console.BufferWidth, 0);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ buffHeight = _consoleDriver.Rows;
|
|
|
|
|
+ buffWidth = _consoleDriver.Cols;
|
|
|
}
|
|
}
|
|
|
- } else {
|
|
|
|
|
- if (winWidth != consoleDriver.Cols || winHeight != lastWindowHeight
|
|
|
|
|
- || buffWidth != consoleDriver.Cols || buffHeight != consoleDriver.Rows) {
|
|
|
|
|
|
|
+ if (EnqueueWindowSizeEvent (
|
|
|
|
|
+ Math.Max (Console.WindowHeight, 0),
|
|
|
|
|
+ Math.Max (Console.WindowWidth, 0),
|
|
|
|
|
+ buffHeight,
|
|
|
|
|
+ buffWidth)) {
|
|
|
|
|
|
|
|
- lastWindowHeight = Math.Max (winHeight, 0);
|
|
|
|
|
- GetWindowSizeEvent (new Size (winWidth, lastWindowHeight));
|
|
|
|
|
- return true;
|
|
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- return false;
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- void GetWindowSizeEvent (Size size)
|
|
|
|
|
- {
|
|
|
|
|
- WindowSizeEvent windowSizeEvent = new WindowSizeEvent () {
|
|
|
|
|
- Size = size
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- inputResultQueue.Enqueue (new InputResult () {
|
|
|
|
|
- EventType = EventType.WindowSize,
|
|
|
|
|
- WindowSizeEvent = windowSizeEvent
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- void GetConsoleInputType (ConsoleKeyInfo consoleKeyInfo)
|
|
|
|
|
- {
|
|
|
|
|
- InputResult inputResult = new InputResult {
|
|
|
|
|
- EventType = EventType.Key
|
|
|
|
|
- };
|
|
|
|
|
- MouseEvent mouseEvent = new MouseEvent ();
|
|
|
|
|
- ConsoleKeyInfo newConsoleKeyInfo = EscSeqUtils.GetConsoleInputKey (consoleKeyInfo);
|
|
|
|
|
- if (inputResult.EventType == EventType.Key) {
|
|
|
|
|
- inputResult.ConsoleKeyInfo = newConsoleKeyInfo;
|
|
|
|
|
- } else {
|
|
|
|
|
- inputResult.MouseEvent = mouseEvent;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- inputResultQueue.Enqueue (inputResult);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- void DecodeEscSeq (ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod)
|
|
|
|
|
- {
|
|
|
|
|
- string c1Control, code, terminating;
|
|
|
|
|
- string [] values;
|
|
|
|
|
- // isKeyMouse is true if it's CSI<, false otherwise
|
|
|
|
|
- bool isKeyMouse;
|
|
|
|
|
- bool isReq;
|
|
|
|
|
- List<MouseFlags> mouseFlags;
|
|
|
|
|
- Point pos;
|
|
|
|
|
- EscSeqUtils.DecodeEscSeq (EscSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed);
|
|
|
|
|
-
|
|
|
|
|
- if (isKeyMouse) {
|
|
|
|
|
- foreach (var mf in mouseFlags) {
|
|
|
|
|
- GetMouseEvent (MapMouseFlags (mf), pos);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ while (true) {
|
|
|
|
|
+ if (_stopTasks) {
|
|
|
return;
|
|
return;
|
|
|
- } else if (isReq) {
|
|
|
|
|
- GetRequestEvent (c1Control, code, values, terminating);
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- InputResult inputResult = new InputResult {
|
|
|
|
|
- EventType = EventType.Key,
|
|
|
|
|
- ConsoleKeyInfo = newConsoleKeyInfo
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- inputResultQueue.Enqueue (inputResult);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos)
|
|
|
|
|
- {
|
|
|
|
|
- GetMouseEvent (MapMouseFlags (mouseFlag), pos);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- MouseButtonState MapMouseFlags (MouseFlags mouseFlags)
|
|
|
|
|
- {
|
|
|
|
|
- MouseButtonState mbs = default;
|
|
|
|
|
- foreach (var flag in Enum.GetValues (mouseFlags.GetType ())) {
|
|
|
|
|
- if (mouseFlags.HasFlag ((MouseFlags)flag)) {
|
|
|
|
|
- switch (flag) {
|
|
|
|
|
- case MouseFlags.Button1Pressed:
|
|
|
|
|
- mbs |= MouseButtonState.Button1Pressed;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.Button1Released:
|
|
|
|
|
- mbs |= MouseButtonState.Button1Released;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.Button1Clicked:
|
|
|
|
|
- mbs |= MouseButtonState.Button1Clicked;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.Button1DoubleClicked:
|
|
|
|
|
- mbs |= MouseButtonState.Button1DoubleClicked;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.Button1TripleClicked:
|
|
|
|
|
- mbs |= MouseButtonState.Button1TripleClicked;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.Button2Pressed:
|
|
|
|
|
- mbs |= MouseButtonState.Button2Pressed;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.Button2Released:
|
|
|
|
|
- mbs |= MouseButtonState.Button2Released;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.Button2Clicked:
|
|
|
|
|
- mbs |= MouseButtonState.Button2Clicked;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.Button2DoubleClicked:
|
|
|
|
|
- mbs |= MouseButtonState.Button2DoubleClicked;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.Button2TripleClicked:
|
|
|
|
|
- mbs |= MouseButtonState.Button2TripleClicked;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.Button3Pressed:
|
|
|
|
|
- mbs |= MouseButtonState.Button3Pressed;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.Button3Released:
|
|
|
|
|
- mbs |= MouseButtonState.Button3Released;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.Button3Clicked:
|
|
|
|
|
- mbs |= MouseButtonState.Button3Clicked;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.Button3DoubleClicked:
|
|
|
|
|
- mbs |= MouseButtonState.Button3DoubleClicked;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.Button3TripleClicked:
|
|
|
|
|
- mbs |= MouseButtonState.Button3TripleClicked;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.WheeledUp:
|
|
|
|
|
- mbs |= MouseButtonState.ButtonWheeledUp;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.WheeledDown:
|
|
|
|
|
- mbs |= MouseButtonState.ButtonWheeledDown;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.WheeledLeft:
|
|
|
|
|
- mbs |= MouseButtonState.ButtonWheeledLeft;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.WheeledRight:
|
|
|
|
|
- mbs |= MouseButtonState.ButtonWheeledRight;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.Button4Pressed:
|
|
|
|
|
- mbs |= MouseButtonState.Button4Pressed;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.Button4Released:
|
|
|
|
|
- mbs |= MouseButtonState.Button4Released;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.Button4Clicked:
|
|
|
|
|
- mbs |= MouseButtonState.Button4Clicked;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.Button4DoubleClicked:
|
|
|
|
|
- mbs |= MouseButtonState.Button4DoubleClicked;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.Button4TripleClicked:
|
|
|
|
|
- mbs |= MouseButtonState.Button4TripleClicked;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.ButtonShift:
|
|
|
|
|
- mbs |= MouseButtonState.ButtonShift;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.ButtonCtrl:
|
|
|
|
|
- mbs |= MouseButtonState.ButtonCtrl;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.ButtonAlt:
|
|
|
|
|
- mbs |= MouseButtonState.ButtonAlt;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.ReportMousePosition:
|
|
|
|
|
- mbs |= MouseButtonState.ReportMousePosition;
|
|
|
|
|
- break;
|
|
|
|
|
- case MouseFlags.AllEvents:
|
|
|
|
|
- mbs |= MouseButtonState.AllEvents;
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
- return mbs;
|
|
|
|
|
|
|
+ _winChange.Wait ();
|
|
|
|
|
+ _winChange.Reset ();
|
|
|
|
|
+ RequestWindowSize ();
|
|
|
|
|
+ _inputReady.Set ();
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- Point lastCursorPosition;
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Enqueue a window size event if the window size has changed.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="winHeight"></param>
|
|
|
|
|
+ /// <param name="winWidth"></param>
|
|
|
|
|
+ /// <param name="buffHeight"></param>
|
|
|
|
|
+ /// <param name="buffWidth"></param>
|
|
|
|
|
+ /// <returns></returns>
|
|
|
|
|
+ bool EnqueueWindowSizeEvent (int winHeight, int winWidth, int buffHeight, int buffWidth)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (winWidth == _consoleDriver.Cols && winHeight == _consoleDriver.Rows) return false;
|
|
|
|
|
+ var w = Math.Max (winWidth, 0);
|
|
|
|
|
+ var h = Math.Max (winHeight, 0);
|
|
|
|
|
+ _inputResultQueue.Enqueue (new InputResult () {
|
|
|
|
|
+ EventType = EventType.WindowSize,
|
|
|
|
|
+ WindowSizeEvent = new WindowSizeEvent () {
|
|
|
|
|
+ Size = new Size (w, h)
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- void GetRequestEvent (string c1Control, string code, string [] values, string terminating)
|
|
|
|
|
- {
|
|
|
|
|
- EventType eventType = new EventType ();
|
|
|
|
|
- switch (terminating) {
|
|
|
|
|
- case "R": // Reports cursor position as CSI r ; c R
|
|
|
|
|
- Point point = new Point {
|
|
|
|
|
- X = int.Parse (values [1]) - 1,
|
|
|
|
|
- Y = int.Parse (values [0]) - 1
|
|
|
|
|
- };
|
|
|
|
|
- if (lastCursorPosition.Y != point.Y) {
|
|
|
|
|
- lastCursorPosition = point;
|
|
|
|
|
- eventType = EventType.WindowPosition;
|
|
|
|
|
- var winPositionEv = new WindowPositionEvent () {
|
|
|
|
|
- CursorPosition = point
|
|
|
|
|
- };
|
|
|
|
|
- inputResultQueue.Enqueue (new InputResult () {
|
|
|
|
|
- EventType = eventType,
|
|
|
|
|
- WindowPositionEvent = winPositionEv
|
|
|
|
|
- });
|
|
|
|
|
- } else {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- break;
|
|
|
|
|
- case "c":
|
|
|
|
|
- try {
|
|
|
|
|
- var parent = EscSeqUtils.GetParentProcess (Process.GetCurrentProcess ());
|
|
|
|
|
- if (parent == null) { Debug.WriteLine ("Not supported!"); }
|
|
|
|
|
- } catch (Exception ex) {
|
|
|
|
|
- Debug.WriteLine (ex.Message);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // Process a CSI sequence received by the driver (key pressed, mouse event, or request/response event)
|
|
|
|
|
+ void ProcessRequestResponse (ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod)
|
|
|
|
|
+ {
|
|
|
|
|
+ // isMouse is true if it's CSI<, false otherwise
|
|
|
|
|
+ EscSeqUtils.DecodeEscSeq (EscSeqRequests, ref newConsoleKeyInfo, ref key, cki, ref mod,
|
|
|
|
|
+ out var c1Control, out var code, out var values, out var terminating,
|
|
|
|
|
+ out var isMouse, out var mouseFlags,
|
|
|
|
|
+ out var pos, out var isReq,
|
|
|
|
|
+ (f, p) => HandleMouseEvent (MapMouseFlags (f), p));
|
|
|
|
|
+
|
|
|
|
|
+ if (isMouse) {
|
|
|
|
|
+ foreach (var mf in mouseFlags) {
|
|
|
|
|
+ HandleMouseEvent (MapMouseFlags (mf), pos);
|
|
|
|
|
+ }
|
|
|
|
|
+ return;
|
|
|
|
|
+ } else if (isReq) {
|
|
|
|
|
+ HandleRequestResponseEvent (c1Control, code, values, terminating);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ HandleKeyboardEvent (newConsoleKeyInfo);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (c1Control == "CSI" && values.Length == 2
|
|
|
|
|
- && values [0] == "1" && values [1] == "0") {
|
|
|
|
|
- // Reports CSI?1;0c ("VT101 with No Options")
|
|
|
|
|
- IsTerminalWithOptions = false;
|
|
|
|
|
- } else {
|
|
|
|
|
- IsTerminalWithOptions = true;
|
|
|
|
|
- }
|
|
|
|
|
- break;
|
|
|
|
|
- case "t":
|
|
|
|
|
- switch (values [0]) {
|
|
|
|
|
- case "8":
|
|
|
|
|
- IsWinChanged (
|
|
|
|
|
- Math.Max (int.Parse (values [1]), 0),
|
|
|
|
|
- Math.Max (int.Parse (values [2]), 0),
|
|
|
|
|
- Math.Max (int.Parse (values [1]), 0),
|
|
|
|
|
- Math.Max (int.Parse (values [2]), 0));
|
|
|
|
|
|
|
+ MouseButtonState MapMouseFlags (MouseFlags mouseFlags)
|
|
|
|
|
+ {
|
|
|
|
|
+ MouseButtonState mbs = default;
|
|
|
|
|
+ foreach (var flag in Enum.GetValues (mouseFlags.GetType ())) {
|
|
|
|
|
+ if (mouseFlags.HasFlag ((MouseFlags)flag)) {
|
|
|
|
|
+ switch (flag) {
|
|
|
|
|
+ case MouseFlags.Button1Pressed:
|
|
|
|
|
+ mbs |= MouseButtonState.Button1Pressed;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.Button1Released:
|
|
|
|
|
+ mbs |= MouseButtonState.Button1Released;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.Button1Clicked:
|
|
|
|
|
+ mbs |= MouseButtonState.Button1Clicked;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.Button1DoubleClicked:
|
|
|
|
|
+ mbs |= MouseButtonState.Button1DoubleClicked;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.Button1TripleClicked:
|
|
|
|
|
+ mbs |= MouseButtonState.Button1TripleClicked;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.Button2Pressed:
|
|
|
|
|
+ mbs |= MouseButtonState.Button2Pressed;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.Button2Released:
|
|
|
|
|
+ mbs |= MouseButtonState.Button2Released;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.Button2Clicked:
|
|
|
|
|
+ mbs |= MouseButtonState.Button2Clicked;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.Button2DoubleClicked:
|
|
|
|
|
+ mbs |= MouseButtonState.Button2DoubleClicked;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.Button2TripleClicked:
|
|
|
|
|
+ mbs |= MouseButtonState.Button2TripleClicked;
|
|
|
break;
|
|
break;
|
|
|
- default:
|
|
|
|
|
- SetRequestedEvent (c1Control, code, values, terminating);
|
|
|
|
|
|
|
+ case MouseFlags.Button3Pressed:
|
|
|
|
|
+ mbs |= MouseButtonState.Button3Pressed;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.Button3Released:
|
|
|
|
|
+ mbs |= MouseButtonState.Button3Released;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.Button3Clicked:
|
|
|
|
|
+ mbs |= MouseButtonState.Button3Clicked;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.Button3DoubleClicked:
|
|
|
|
|
+ mbs |= MouseButtonState.Button3DoubleClicked;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.Button3TripleClicked:
|
|
|
|
|
+ mbs |= MouseButtonState.Button3TripleClicked;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.WheeledUp:
|
|
|
|
|
+ mbs |= MouseButtonState.ButtonWheeledUp;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.WheeledDown:
|
|
|
|
|
+ mbs |= MouseButtonState.ButtonWheeledDown;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.WheeledLeft:
|
|
|
|
|
+ mbs |= MouseButtonState.ButtonWheeledLeft;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.WheeledRight:
|
|
|
|
|
+ mbs |= MouseButtonState.ButtonWheeledRight;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.Button4Pressed:
|
|
|
|
|
+ mbs |= MouseButtonState.Button4Pressed;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.Button4Released:
|
|
|
|
|
+ mbs |= MouseButtonState.Button4Released;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.Button4Clicked:
|
|
|
|
|
+ mbs |= MouseButtonState.Button4Clicked;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.Button4DoubleClicked:
|
|
|
|
|
+ mbs |= MouseButtonState.Button4DoubleClicked;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.Button4TripleClicked:
|
|
|
|
|
+ mbs |= MouseButtonState.Button4TripleClicked;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.ButtonShift:
|
|
|
|
|
+ mbs |= MouseButtonState.ButtonShift;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.ButtonCtrl:
|
|
|
|
|
+ mbs |= MouseButtonState.ButtonCtrl;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.ButtonAlt:
|
|
|
|
|
+ mbs |= MouseButtonState.ButtonAlt;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.ReportMousePosition:
|
|
|
|
|
+ mbs |= MouseButtonState.ReportMousePosition;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case MouseFlags.AllEvents:
|
|
|
|
|
+ mbs |= MouseButtonState.AllEvents;
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
- break;
|
|
|
|
|
- default:
|
|
|
|
|
- SetRequestedEvent (c1Control, code, values, terminating);
|
|
|
|
|
- break;
|
|
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- inputReady.Set ();
|
|
|
|
|
}
|
|
}
|
|
|
|
|
+ return mbs;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- void SetRequestedEvent (string c1Control, string code, string [] values, string terminating)
|
|
|
|
|
- {
|
|
|
|
|
- EventType eventType = EventType.RequestResponse;
|
|
|
|
|
- var requestRespEv = new RequestResponseEvent () {
|
|
|
|
|
- ResultTuple = (c1Control, code, values, terminating)
|
|
|
|
|
- };
|
|
|
|
|
- inputResultQueue.Enqueue (new InputResult () {
|
|
|
|
|
- EventType = eventType,
|
|
|
|
|
- RequestResponseEvent = requestRespEv
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ Point _lastCursorPosition;
|
|
|
|
|
|
|
|
- void GetMouseEvent (MouseButtonState buttonState, Point pos)
|
|
|
|
|
- {
|
|
|
|
|
- MouseEvent mouseEvent = new MouseEvent () {
|
|
|
|
|
- Position = pos,
|
|
|
|
|
- ButtonState = buttonState,
|
|
|
|
|
|
|
+ void HandleRequestResponseEvent (string c1Control, string code, string [] values, string terminating)
|
|
|
|
|
+ {
|
|
|
|
|
+ switch (terminating) {
|
|
|
|
|
+ // BUGBUG: I can't find where we send a request for cursor position (ESC[?6n), so I'm not sure if this is needed.
|
|
|
|
|
+ case EscSeqUtils.CSI_RequestCursorPositionReport_Terminator:
|
|
|
|
|
+ Point point = new Point {
|
|
|
|
|
+ X = int.Parse (values [1]) - 1,
|
|
|
|
|
+ Y = int.Parse (values [0]) - 1
|
|
|
};
|
|
};
|
|
|
-
|
|
|
|
|
- inputResultQueue.Enqueue (new InputResult () {
|
|
|
|
|
- EventType = EventType.Mouse,
|
|
|
|
|
- MouseEvent = mouseEvent
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- inputReady.Set ();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public enum EventType {
|
|
|
|
|
- Key = 1,
|
|
|
|
|
- Mouse = 2,
|
|
|
|
|
- WindowSize = 3,
|
|
|
|
|
- WindowPosition = 4,
|
|
|
|
|
- RequestResponse = 5
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- [Flags]
|
|
|
|
|
- public enum MouseButtonState {
|
|
|
|
|
- Button1Pressed = 0x1,
|
|
|
|
|
- Button1Released = 0x2,
|
|
|
|
|
- Button1Clicked = 0x4,
|
|
|
|
|
- Button1DoubleClicked = 0x8,
|
|
|
|
|
- Button1TripleClicked = 0x10,
|
|
|
|
|
- Button2Pressed = 0x20,
|
|
|
|
|
- Button2Released = 0x40,
|
|
|
|
|
- Button2Clicked = 0x80,
|
|
|
|
|
- Button2DoubleClicked = 0x100,
|
|
|
|
|
- Button2TripleClicked = 0x200,
|
|
|
|
|
- Button3Pressed = 0x400,
|
|
|
|
|
- Button3Released = 0x800,
|
|
|
|
|
- Button3Clicked = 0x1000,
|
|
|
|
|
- Button3DoubleClicked = 0x2000,
|
|
|
|
|
- Button3TripleClicked = 0x4000,
|
|
|
|
|
- ButtonWheeledUp = 0x8000,
|
|
|
|
|
- ButtonWheeledDown = 0x10000,
|
|
|
|
|
- ButtonWheeledLeft = 0x20000,
|
|
|
|
|
- ButtonWheeledRight = 0x40000,
|
|
|
|
|
- Button4Pressed = 0x80000,
|
|
|
|
|
- Button4Released = 0x100000,
|
|
|
|
|
- Button4Clicked = 0x200000,
|
|
|
|
|
- Button4DoubleClicked = 0x400000,
|
|
|
|
|
- Button4TripleClicked = 0x800000,
|
|
|
|
|
- ButtonShift = 0x1000000,
|
|
|
|
|
- ButtonCtrl = 0x2000000,
|
|
|
|
|
- ButtonAlt = 0x4000000,
|
|
|
|
|
- ReportMousePosition = 0x8000000,
|
|
|
|
|
- AllEvents = -1
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public struct MouseEvent {
|
|
|
|
|
- public Point Position;
|
|
|
|
|
- public MouseButtonState ButtonState;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public struct WindowSizeEvent {
|
|
|
|
|
- public Size Size;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public struct WindowPositionEvent {
|
|
|
|
|
- public int Top;
|
|
|
|
|
- public int Left;
|
|
|
|
|
- public Point CursorPosition;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public struct RequestResponseEvent {
|
|
|
|
|
- public (string c1Control, string code, string [] values, string terminating) ResultTuple;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public struct InputResult {
|
|
|
|
|
- public EventType EventType;
|
|
|
|
|
- public ConsoleKeyInfo ConsoleKeyInfo;
|
|
|
|
|
- public MouseEvent MouseEvent;
|
|
|
|
|
- public WindowSizeEvent WindowSizeEvent;
|
|
|
|
|
- public WindowPositionEvent WindowPositionEvent;
|
|
|
|
|
- public RequestResponseEvent RequestResponseEvent;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- internal class NetDriver : ConsoleDriver {
|
|
|
|
|
- const int COLOR_BLACK = 30;
|
|
|
|
|
- const int COLOR_RED = 31;
|
|
|
|
|
- const int COLOR_GREEN = 32;
|
|
|
|
|
- const int COLOR_YELLOW = 33;
|
|
|
|
|
- const int COLOR_BLUE = 34;
|
|
|
|
|
- const int COLOR_MAGENTA = 35;
|
|
|
|
|
- const int COLOR_CYAN = 36;
|
|
|
|
|
- const int COLOR_WHITE = 37;
|
|
|
|
|
- const int COLOR_BRIGHT_BLACK = 90;
|
|
|
|
|
- const int COLOR_BRIGHT_RED = 91;
|
|
|
|
|
- const int COLOR_BRIGHT_GREEN = 92;
|
|
|
|
|
- const int COLOR_BRIGHT_YELLOW = 93;
|
|
|
|
|
- const int COLOR_BRIGHT_BLUE = 94;
|
|
|
|
|
- const int COLOR_BRIGHT_MAGENTA = 95;
|
|
|
|
|
- const int COLOR_BRIGHT_CYAN = 96;
|
|
|
|
|
- const int COLOR_BRIGHT_WHITE = 97;
|
|
|
|
|
-
|
|
|
|
|
- int cols, rows, left, top;
|
|
|
|
|
-
|
|
|
|
|
- public override int Cols => cols;
|
|
|
|
|
- public override int Rows => rows;
|
|
|
|
|
- public override int Left => left;
|
|
|
|
|
- public override int Top => top;
|
|
|
|
|
- public override bool EnableConsoleScrolling { get; set; }
|
|
|
|
|
- public NetWinVTConsole NetWinConsole { get; }
|
|
|
|
|
- public bool IsWinPlatform { get; }
|
|
|
|
|
- public override IClipboard Clipboard { get; }
|
|
|
|
|
- public override int [,,] Contents => contents;
|
|
|
|
|
-
|
|
|
|
|
- int largestBufferHeight;
|
|
|
|
|
-
|
|
|
|
|
- public NetDriver ()
|
|
|
|
|
- {
|
|
|
|
|
- var p = Environment.OSVersion.Platform;
|
|
|
|
|
- if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
|
|
|
|
|
- IsWinPlatform = true;
|
|
|
|
|
- NetWinConsole = new NetWinVTConsole ();
|
|
|
|
|
- }
|
|
|
|
|
- if (IsWinPlatform) {
|
|
|
|
|
- Clipboard = new WindowsClipboard ();
|
|
|
|
|
- } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
|
|
|
|
|
- Clipboard = new MacOSXClipboard ();
|
|
|
|
|
|
|
+ if (_lastCursorPosition.Y != point.Y) {
|
|
|
|
|
+ _lastCursorPosition = point;
|
|
|
|
|
+ var eventType = EventType.WindowPosition;
|
|
|
|
|
+ var winPositionEv = new WindowPositionEvent () {
|
|
|
|
|
+ CursorPosition = point
|
|
|
|
|
+ };
|
|
|
|
|
+ _inputResultQueue.Enqueue (new InputResult () {
|
|
|
|
|
+ EventType = eventType,
|
|
|
|
|
+ WindowPositionEvent = winPositionEv
|
|
|
|
|
+ });
|
|
|
} else {
|
|
} else {
|
|
|
- if (CursesDriver.Is_WSL_Platform ()) {
|
|
|
|
|
- Clipboard = new WSLClipboard ();
|
|
|
|
|
- } else {
|
|
|
|
|
- Clipboard = new CursesClipboard ();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
|
|
|
|
|
- int [,,] contents;
|
|
|
|
|
- bool [] dirtyLine;
|
|
|
|
|
-
|
|
|
|
|
- static bool sync = false;
|
|
|
|
|
-
|
|
|
|
|
- // Current row, and current col, tracked by Move/AddCh only
|
|
|
|
|
- int ccol, crow;
|
|
|
|
|
-
|
|
|
|
|
- public override void Move (int col, int row)
|
|
|
|
|
- {
|
|
|
|
|
- ccol = col;
|
|
|
|
|
- crow = row;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public override void AddRune (Rune rune)
|
|
|
|
|
- {
|
|
|
|
|
- if (contents.Length != Rows * Cols * 3) {
|
|
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- rune = rune.MakePrintable ();
|
|
|
|
|
- var runeWidth = rune.GetColumns ();
|
|
|
|
|
- var validClip = IsValidContent (ccol, crow, Clip);
|
|
|
|
|
-
|
|
|
|
|
- if (validClip) {
|
|
|
|
|
- if (runeWidth == 0 && ccol > 0) {
|
|
|
|
|
- var r = contents [crow, ccol - 1, 0];
|
|
|
|
|
- var s = new string (new char [] { (char)r, (char)rune.Value });
|
|
|
|
|
- string sn;
|
|
|
|
|
- if (!s.IsNormalized ()) {
|
|
|
|
|
- sn = s.Normalize ();
|
|
|
|
|
- } else {
|
|
|
|
|
- sn = s;
|
|
|
|
|
- }
|
|
|
|
|
- var c = sn [0];
|
|
|
|
|
- contents [crow, ccol - 1, 0] = c;
|
|
|
|
|
- contents [crow, ccol - 1, 1] = CurrentAttribute;
|
|
|
|
|
- contents [crow, ccol - 1, 2] = 1;
|
|
|
|
|
-
|
|
|
|
|
- } else {
|
|
|
|
|
- if (runeWidth < 2 && ccol > 0
|
|
|
|
|
- && ((Rune)(char)contents [crow, ccol - 1, 0]).GetColumns () > 1) {
|
|
|
|
|
-
|
|
|
|
|
- contents [crow, ccol - 1, 0] = (int)(uint)' ';
|
|
|
|
|
-
|
|
|
|
|
- } else if (runeWidth < 2 && ccol <= Clip.Right - 1
|
|
|
|
|
- && ((Rune)(char)contents [crow, ccol, 0]).GetColumns () > 1) {
|
|
|
|
|
-
|
|
|
|
|
- contents [crow, ccol + 1, 0] = (int)(uint)' ';
|
|
|
|
|
- contents [crow, ccol + 1, 2] = 1;
|
|
|
|
|
-
|
|
|
|
|
- }
|
|
|
|
|
- if (runeWidth > 1 && ccol == Clip.Right - 1) {
|
|
|
|
|
- contents [crow, ccol, 0] = (int)(uint)' ';
|
|
|
|
|
- } else {
|
|
|
|
|
- contents [crow, ccol, 0] = (int)(uint)rune.Value;
|
|
|
|
|
- }
|
|
|
|
|
- contents [crow, ccol, 1] = CurrentAttribute;
|
|
|
|
|
- contents [crow, ccol, 2] = 1;
|
|
|
|
|
-
|
|
|
|
|
- }
|
|
|
|
|
- dirtyLine [crow] = true;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ break;
|
|
|
|
|
|
|
|
- if (runeWidth < 0 || runeWidth > 0) {
|
|
|
|
|
- ccol++;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (runeWidth > 1) {
|
|
|
|
|
- if (validClip && ccol < Clip.Right) {
|
|
|
|
|
- contents [crow, ccol, 1] = CurrentAttribute;
|
|
|
|
|
- contents [crow, ccol, 2] = 0;
|
|
|
|
|
- }
|
|
|
|
|
- ccol++;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (sync) {
|
|
|
|
|
- UpdateScreen ();
|
|
|
|
|
|
|
+ case EscSeqUtils.CSI_ReportTerminalSizeInChars_Terminator:
|
|
|
|
|
+ switch (values [0]) {
|
|
|
|
|
+ case EscSeqUtils.CSI_ReportTerminalSizeInChars_ResponseValue:
|
|
|
|
|
+ EnqueueWindowSizeEvent (
|
|
|
|
|
+ Math.Max (int.Parse (values [1]), 0),
|
|
|
|
|
+ Math.Max (int.Parse (values [2]), 0),
|
|
|
|
|
+ Math.Max (int.Parse (values [1]), 0),
|
|
|
|
|
+ Math.Max (int.Parse (values [2]), 0));
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ EnqueueRequestResponseEvent (c1Control, code, values, terminating);
|
|
|
|
|
+ break;
|
|
|
}
|
|
}
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ EnqueueRequestResponseEvent (c1Control, code, values, terminating);
|
|
|
|
|
+ break;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public override void AddStr (string str)
|
|
|
|
|
- {
|
|
|
|
|
- foreach (var rune in str.EnumerateRunes ())
|
|
|
|
|
- AddRune (rune);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ _inputReady.Set ();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- public override void End ()
|
|
|
|
|
- {
|
|
|
|
|
- mainLoop.netEvents.StopTasks ();
|
|
|
|
|
|
|
+ void EnqueueRequestResponseEvent (string c1Control, string code, string [] values, string terminating)
|
|
|
|
|
+ {
|
|
|
|
|
+ EventType eventType = EventType.RequestResponse;
|
|
|
|
|
+ var requestRespEv = new RequestResponseEvent () {
|
|
|
|
|
+ ResultTuple = (c1Control, code, values, terminating)
|
|
|
|
|
+ };
|
|
|
|
|
+ _inputResultQueue.Enqueue (new InputResult () {
|
|
|
|
|
+ EventType = eventType,
|
|
|
|
|
+ RequestResponseEvent = requestRespEv
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (IsWinPlatform) {
|
|
|
|
|
- NetWinConsole.Cleanup ();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ void HandleMouseEvent (MouseButtonState buttonState, Point pos)
|
|
|
|
|
+ {
|
|
|
|
|
+ MouseEvent mouseEvent = new MouseEvent () {
|
|
|
|
|
+ Position = pos,
|
|
|
|
|
+ ButtonState = buttonState,
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- StopReportingMouseMoves ();
|
|
|
|
|
- Console.ResetColor ();
|
|
|
|
|
|
|
+ _inputResultQueue.Enqueue (new InputResult () {
|
|
|
|
|
+ EventType = EventType.Mouse,
|
|
|
|
|
+ MouseEvent = mouseEvent
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
- //Disable alternative screen buffer.
|
|
|
|
|
- Console.Out.Write ("\x1b[?1049l");
|
|
|
|
|
|
|
+ _inputReady.Set ();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- //Set cursor key to cursor.
|
|
|
|
|
- Console.Out.Write ("\x1b[?25h");
|
|
|
|
|
|
|
+ public enum EventType {
|
|
|
|
|
+ Key = 1,
|
|
|
|
|
+ Mouse = 2,
|
|
|
|
|
+ WindowSize = 3,
|
|
|
|
|
+ WindowPosition = 4,
|
|
|
|
|
+ RequestResponse = 5
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- Console.Out.Close ();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ [Flags]
|
|
|
|
|
+ public enum MouseButtonState {
|
|
|
|
|
+ Button1Pressed = 0x1,
|
|
|
|
|
+ Button1Released = 0x2,
|
|
|
|
|
+ Button1Clicked = 0x4,
|
|
|
|
|
+ Button1DoubleClicked = 0x8,
|
|
|
|
|
+ Button1TripleClicked = 0x10,
|
|
|
|
|
+ Button2Pressed = 0x20,
|
|
|
|
|
+ Button2Released = 0x40,
|
|
|
|
|
+ Button2Clicked = 0x80,
|
|
|
|
|
+ Button2DoubleClicked = 0x100,
|
|
|
|
|
+ Button2TripleClicked = 0x200,
|
|
|
|
|
+ Button3Pressed = 0x400,
|
|
|
|
|
+ Button3Released = 0x800,
|
|
|
|
|
+ Button3Clicked = 0x1000,
|
|
|
|
|
+ Button3DoubleClicked = 0x2000,
|
|
|
|
|
+ Button3TripleClicked = 0x4000,
|
|
|
|
|
+ ButtonWheeledUp = 0x8000,
|
|
|
|
|
+ ButtonWheeledDown = 0x10000,
|
|
|
|
|
+ ButtonWheeledLeft = 0x20000,
|
|
|
|
|
+ ButtonWheeledRight = 0x40000,
|
|
|
|
|
+ Button4Pressed = 0x80000,
|
|
|
|
|
+ Button4Released = 0x100000,
|
|
|
|
|
+ Button4Clicked = 0x200000,
|
|
|
|
|
+ Button4DoubleClicked = 0x400000,
|
|
|
|
|
+ Button4TripleClicked = 0x800000,
|
|
|
|
|
+ ButtonShift = 0x1000000,
|
|
|
|
|
+ ButtonCtrl = 0x2000000,
|
|
|
|
|
+ ButtonAlt = 0x4000000,
|
|
|
|
|
+ ReportMousePosition = 0x8000000,
|
|
|
|
|
+ AllEvents = -1
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- public override Attribute MakeColor (Color foreground, Color background)
|
|
|
|
|
- {
|
|
|
|
|
- return MakeColor ((ConsoleColor)foreground, (ConsoleColor)background);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ public struct MouseEvent {
|
|
|
|
|
+ public Point Position;
|
|
|
|
|
+ public MouseButtonState ButtonState;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- static Attribute MakeColor (ConsoleColor f, ConsoleColor b)
|
|
|
|
|
- {
|
|
|
|
|
- // Encode the colors into the int value.
|
|
|
|
|
- return new Attribute (
|
|
|
|
|
- value: ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff),
|
|
|
|
|
- foreground: (Color)f,
|
|
|
|
|
- background: (Color)b
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ public struct WindowSizeEvent {
|
|
|
|
|
+ public Size Size;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- public override void Init (Action terminalResized)
|
|
|
|
|
- {
|
|
|
|
|
- TerminalResized = terminalResized;
|
|
|
|
|
|
|
+ public struct WindowPositionEvent {
|
|
|
|
|
+ public int Top;
|
|
|
|
|
+ public int Left;
|
|
|
|
|
+ public Point CursorPosition;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- //Enable alternative screen buffer.
|
|
|
|
|
- Console.Out.Write ("\x1b[?1049h");
|
|
|
|
|
|
|
+ public struct RequestResponseEvent {
|
|
|
|
|
+ public (string c1Control, string code, string [] values, string terminating) ResultTuple;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- //Set cursor key to application.
|
|
|
|
|
- Console.Out.Write ("\x1b[?25l");
|
|
|
|
|
|
|
+ public struct InputResult {
|
|
|
|
|
+ public EventType EventType;
|
|
|
|
|
+ public ConsoleKeyInfo ConsoleKeyInfo;
|
|
|
|
|
+ public MouseEvent MouseEvent;
|
|
|
|
|
+ public WindowSizeEvent WindowSizeEvent;
|
|
|
|
|
+ public WindowPositionEvent WindowPositionEvent;
|
|
|
|
|
+ public RequestResponseEvent RequestResponseEvent;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- Console.TreatControlCAsInput = true;
|
|
|
|
|
|
|
+ void HandleKeyboardEvent (ConsoleKeyInfo cki)
|
|
|
|
|
+ {
|
|
|
|
|
+ InputResult inputResult = new InputResult {
|
|
|
|
|
+ EventType = EventType.Key,
|
|
|
|
|
+ ConsoleKeyInfo = cki
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- if (EnableConsoleScrolling) {
|
|
|
|
|
- largestBufferHeight = Console.BufferHeight;
|
|
|
|
|
- } else {
|
|
|
|
|
- largestBufferHeight = Console.WindowHeight;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ _inputResultQueue.Enqueue (inputResult);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+internal class NetDriver : ConsoleDriver {
|
|
|
|
|
+ const int COLOR_BLACK = 30;
|
|
|
|
|
+ const int COLOR_RED = 31;
|
|
|
|
|
+ const int COLOR_GREEN = 32;
|
|
|
|
|
+ const int COLOR_YELLOW = 33;
|
|
|
|
|
+ const int COLOR_BLUE = 34;
|
|
|
|
|
+ const int COLOR_MAGENTA = 35;
|
|
|
|
|
+ const int COLOR_CYAN = 36;
|
|
|
|
|
+ const int COLOR_WHITE = 37;
|
|
|
|
|
+ const int COLOR_BRIGHT_BLACK = 90;
|
|
|
|
|
+ const int COLOR_BRIGHT_RED = 91;
|
|
|
|
|
+ const int COLOR_BRIGHT_GREEN = 92;
|
|
|
|
|
+ const int COLOR_BRIGHT_YELLOW = 93;
|
|
|
|
|
+ const int COLOR_BRIGHT_BLUE = 94;
|
|
|
|
|
+ const int COLOR_BRIGHT_MAGENTA = 95;
|
|
|
|
|
+ const int COLOR_BRIGHT_CYAN = 96;
|
|
|
|
|
+ const int COLOR_BRIGHT_WHITE = 97;
|
|
|
|
|
+
|
|
|
|
|
+ public NetWinVTConsole NetWinConsole { get; private set; }
|
|
|
|
|
+ public bool IsWinPlatform { get; private set; }
|
|
|
|
|
+
|
|
|
|
|
+ int _largestBufferHeight;
|
|
|
|
|
+
|
|
|
|
|
+ public NetDriver ()
|
|
|
|
|
+ {
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- cols = Console.WindowWidth;
|
|
|
|
|
- rows = largestBufferHeight;
|
|
|
|
|
|
|
+ public override void End ()
|
|
|
|
|
+ {
|
|
|
|
|
+ _mainLoop._netEvents.StopTasks ();
|
|
|
|
|
|
|
|
- CurrentAttribute = MakeColor (Color.White, Color.Black);
|
|
|
|
|
- InitalizeColorSchemes ();
|
|
|
|
|
|
|
+ if (IsWinPlatform) {
|
|
|
|
|
+ NetWinConsole.Cleanup ();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- CurrentAttribute = MakeColor (Color.White, Color.Black);
|
|
|
|
|
- InitalizeColorSchemes ();
|
|
|
|
|
|
|
+ StopReportingMouseMoves ();
|
|
|
|
|
+ Console.ResetColor ();
|
|
|
|
|
|
|
|
- ResizeScreen ();
|
|
|
|
|
- UpdateOffScreen ();
|
|
|
|
|
|
|
+ //Disable alternative screen buffer.
|
|
|
|
|
+ Console.Out.Write (EscSeqUtils.CSI_RestoreCursorAndActivateAltBufferWithBackscroll);
|
|
|
|
|
|
|
|
- StartReportingMouseMoves ();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ //Set cursor key to cursor.
|
|
|
|
|
+ Console.Out.Write (EscSeqUtils.CSI_ShowCursor);
|
|
|
|
|
|
|
|
- public override void ResizeScreen ()
|
|
|
|
|
- {
|
|
|
|
|
- if (!EnableConsoleScrolling) {
|
|
|
|
|
- if (Console.WindowHeight > 0) {
|
|
|
|
|
- // Not supported on Unix.
|
|
|
|
|
- if (IsWinPlatform) {
|
|
|
|
|
- // Can raise an exception while is still resizing.
|
|
|
|
|
- try {
|
|
|
|
|
-#pragma warning disable CA1416
|
|
|
|
|
- Console.CursorTop = 0;
|
|
|
|
|
- Console.CursorLeft = 0;
|
|
|
|
|
- Console.WindowTop = 0;
|
|
|
|
|
- Console.WindowLeft = 0;
|
|
|
|
|
- if (Console.WindowHeight > Rows) {
|
|
|
|
|
- Console.SetWindowSize (Cols, Rows);
|
|
|
|
|
- }
|
|
|
|
|
- Console.SetBufferSize (Cols, Rows);
|
|
|
|
|
-#pragma warning restore CA1416
|
|
|
|
|
- } catch (System.IO.IOException) {
|
|
|
|
|
- setClip ();
|
|
|
|
|
- } catch (ArgumentOutOfRangeException) {
|
|
|
|
|
- setClip ();
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- Console.Out.Write ($"\x1b[8;{Rows};{Cols}t");
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- if (IsWinPlatform) {
|
|
|
|
|
- if (Console.WindowHeight > 0) {
|
|
|
|
|
- // Can raise an exception while is still resizing.
|
|
|
|
|
- try {
|
|
|
|
|
-#pragma warning disable CA1416
|
|
|
|
|
- Console.CursorTop = 0;
|
|
|
|
|
- Console.CursorLeft = 0;
|
|
|
|
|
- if (Console.WindowHeight > Rows) {
|
|
|
|
|
- Console.SetWindowSize (Cols, Rows);
|
|
|
|
|
- }
|
|
|
|
|
- Console.SetBufferSize (Cols, Rows);
|
|
|
|
|
-#pragma warning restore CA1416
|
|
|
|
|
- } catch (System.IO.IOException) {
|
|
|
|
|
- setClip ();
|
|
|
|
|
- } catch (ArgumentOutOfRangeException) {
|
|
|
|
|
- setClip ();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- Console.Out.Write ($"\x1b[30;{Rows};{Cols}t");
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- setClip ();
|
|
|
|
|
|
|
+ Console.Out.Close ();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- void setClip ()
|
|
|
|
|
- {
|
|
|
|
|
- Clip = new Rect (0, 0, Cols, Rows);
|
|
|
|
|
|
|
+ public override void Init (Action terminalResized)
|
|
|
|
|
+ {
|
|
|
|
|
+ var p = Environment.OSVersion.Platform;
|
|
|
|
|
+ if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
|
|
|
|
|
+ IsWinPlatform = true;
|
|
|
|
|
+ try {
|
|
|
|
|
+ NetWinConsole = new NetWinVTConsole ();
|
|
|
|
|
+ } catch (ApplicationException) {
|
|
|
|
|
+ // Likely running as a unit test, or in a non-interactive session.
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- public override void UpdateOffScreen ()
|
|
|
|
|
- {
|
|
|
|
|
- contents = new int [Rows, Cols, 3];
|
|
|
|
|
- dirtyLine = new bool [Rows];
|
|
|
|
|
-
|
|
|
|
|
- lock (contents) {
|
|
|
|
|
- // Can raise an exception while is still resizing.
|
|
|
|
|
- try {
|
|
|
|
|
- for (int row = 0; row < rows; row++) {
|
|
|
|
|
- for (int c = 0; c < cols; c++) {
|
|
|
|
|
- contents [row, c, 0] = ' ';
|
|
|
|
|
- contents [row, c, 1] = (ushort)Colors.TopLevel.Normal;
|
|
|
|
|
- contents [row, c, 2] = 0;
|
|
|
|
|
- dirtyLine [row] = true;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- } catch (IndexOutOfRangeException) { }
|
|
|
|
|
|
|
+ if (IsWinPlatform) {
|
|
|
|
|
+ Clipboard = new WindowsClipboard ();
|
|
|
|
|
+ } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
|
|
|
|
|
+ Clipboard = new MacOSXClipboard ();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if (CursesDriver.Is_WSL_Platform ()) {
|
|
|
|
|
+ Clipboard = new WSLClipboard ();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Clipboard = new CursesClipboard ();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public override Attribute MakeAttribute (Color fore, Color back)
|
|
|
|
|
- {
|
|
|
|
|
- return MakeColor ((ConsoleColor)fore, (ConsoleColor)back);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ TerminalResized = terminalResized;
|
|
|
|
|
|
|
|
- public override void Refresh ()
|
|
|
|
|
- {
|
|
|
|
|
- UpdateScreen ();
|
|
|
|
|
- UpdateCursor ();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public override void UpdateScreen ()
|
|
|
|
|
- {
|
|
|
|
|
- if (winChanging || Console.WindowHeight < 1 || contents.Length != Rows * Cols * 3
|
|
|
|
|
- || (!EnableConsoleScrolling && Rows != Console.WindowHeight)
|
|
|
|
|
- || (EnableConsoleScrolling && Rows != largestBufferHeight)) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- int top = 0;
|
|
|
|
|
- int left = 0;
|
|
|
|
|
- int rows = Rows;
|
|
|
|
|
- int cols = Cols;
|
|
|
|
|
- System.Text.StringBuilder output = new System.Text.StringBuilder ();
|
|
|
|
|
- int redrawAttr = -1;
|
|
|
|
|
- var lastCol = -1;
|
|
|
|
|
-
|
|
|
|
|
- GetCursorVisibility (out CursorVisibility savedVisibitity);
|
|
|
|
|
- SetCursorVisibility (CursorVisibility.Invisible);
|
|
|
|
|
-
|
|
|
|
|
- for (int row = top; row < rows; row++) {
|
|
|
|
|
- if (Console.WindowHeight < 1) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (!dirtyLine [row]) {
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
- if (!SetCursorPosition (0, row)) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- dirtyLine [row] = false;
|
|
|
|
|
- output.Clear ();
|
|
|
|
|
- for (int col = left; col < cols; col++) {
|
|
|
|
|
- lastCol = -1;
|
|
|
|
|
- var outputWidth = 0;
|
|
|
|
|
- for (; col < cols; col++) {
|
|
|
|
|
- if (contents [row, col, 2] == 0) {
|
|
|
|
|
- if (output.Length > 0) {
|
|
|
|
|
- SetCursorPosition (lastCol, row);
|
|
|
|
|
- Console.Write (output);
|
|
|
|
|
- output.Clear ();
|
|
|
|
|
- lastCol += outputWidth;
|
|
|
|
|
- outputWidth = 0;
|
|
|
|
|
- } else if (lastCol == -1) {
|
|
|
|
|
- lastCol = col;
|
|
|
|
|
- }
|
|
|
|
|
- if (lastCol + 1 < cols)
|
|
|
|
|
- lastCol++;
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (NetWinConsole != null) {
|
|
|
|
|
+ //Enable alternative screen buffer.
|
|
|
|
|
+ Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll);
|
|
|
|
|
|
|
|
- if (lastCol == -1)
|
|
|
|
|
- lastCol = col;
|
|
|
|
|
|
|
+ //Set cursor key to application.
|
|
|
|
|
+ Console.Out.Write (EscSeqUtils.CSI_HideCursor);
|
|
|
|
|
|
|
|
- var attr = contents [row, col, 1];
|
|
|
|
|
- if (attr != redrawAttr) {
|
|
|
|
|
- redrawAttr = attr;
|
|
|
|
|
- output.Append (WriteAttributes (attr));
|
|
|
|
|
- }
|
|
|
|
|
- outputWidth++;
|
|
|
|
|
- var rune = (Rune)contents [row, col, 0];
|
|
|
|
|
- char [] spair;
|
|
|
|
|
- if (rune.DecodeSurrogatePair (out spair)) {
|
|
|
|
|
- output.Append (spair);
|
|
|
|
|
- } else {
|
|
|
|
|
- output.Append ((char)rune.Value);
|
|
|
|
|
- }
|
|
|
|
|
- contents [row, col, 2] = 0;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- if (output.Length > 0) {
|
|
|
|
|
- SetCursorPosition (lastCol, row);
|
|
|
|
|
- Console.Write (output);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- SetCursorPosition (0, 0);
|
|
|
|
|
- SetCursorVisibility (savedVisibitity);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ Console.TreatControlCAsInput = true;
|
|
|
|
|
|
|
|
- void SetVirtualCursorPosition (int col, int row)
|
|
|
|
|
- {
|
|
|
|
|
- Console.Out.Write ($"\x1b[{row + 1};{col + 1}H");
|
|
|
|
|
|
|
+ Cols = Console.WindowWidth;
|
|
|
|
|
+ Rows = Console.WindowHeight;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Simluate
|
|
|
|
|
+ Cols = 80;
|
|
|
|
|
+ Rows = 25;
|
|
|
|
|
+ _largestBufferHeight = Rows;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- System.Text.StringBuilder WriteAttributes (int attr)
|
|
|
|
|
- {
|
|
|
|
|
- const string CSI = "\x1b[";
|
|
|
|
|
- int bg = 0;
|
|
|
|
|
- int fg = 0;
|
|
|
|
|
- System.Text.StringBuilder sb = new System.Text.StringBuilder ();
|
|
|
|
|
-
|
|
|
|
|
- IEnumerable<int> values = Enum.GetValues (typeof (ConsoleColor))
|
|
|
|
|
- .OfType<ConsoleColor> ()
|
|
|
|
|
- .Select (s => (int)s);
|
|
|
|
|
- if (values.Contains (attr & 0xffff)) {
|
|
|
|
|
- bg = MapColors ((ConsoleColor)(attr & 0xffff), false);
|
|
|
|
|
- }
|
|
|
|
|
- if (values.Contains ((attr >> 16) & 0xffff)) {
|
|
|
|
|
- fg = MapColors ((ConsoleColor)((attr >> 16) & 0xffff));
|
|
|
|
|
- }
|
|
|
|
|
- sb.Append ($"{CSI}{bg};{fg}m");
|
|
|
|
|
|
|
+ ResizeScreen ();
|
|
|
|
|
+ ClearContents ();
|
|
|
|
|
+ CurrentAttribute = MakeColor (Color.White, Color.Black);
|
|
|
|
|
+ InitializeColorSchemes ();
|
|
|
|
|
|
|
|
- return sb;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ StartReportingMouseMoves ();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- int MapColors (ConsoleColor color, bool isForeground = true)
|
|
|
|
|
- {
|
|
|
|
|
- switch (color) {
|
|
|
|
|
- case ConsoleColor.Black:
|
|
|
|
|
- return isForeground ? COLOR_BLACK : COLOR_BLACK + 10;
|
|
|
|
|
- case ConsoleColor.DarkBlue:
|
|
|
|
|
- return isForeground ? COLOR_BLUE : COLOR_BLUE + 10;
|
|
|
|
|
- case ConsoleColor.DarkGreen:
|
|
|
|
|
- return isForeground ? COLOR_GREEN : COLOR_GREEN + 10;
|
|
|
|
|
- case ConsoleColor.DarkCyan:
|
|
|
|
|
- return isForeground ? COLOR_CYAN : COLOR_CYAN + 10;
|
|
|
|
|
- case ConsoleColor.DarkRed:
|
|
|
|
|
- return isForeground ? COLOR_RED : COLOR_RED + 10;
|
|
|
|
|
- case ConsoleColor.DarkMagenta:
|
|
|
|
|
- return isForeground ? COLOR_MAGENTA : COLOR_MAGENTA + 10;
|
|
|
|
|
- case ConsoleColor.DarkYellow:
|
|
|
|
|
- return isForeground ? COLOR_YELLOW : COLOR_YELLOW + 10;
|
|
|
|
|
- case ConsoleColor.Gray:
|
|
|
|
|
- return isForeground ? COLOR_WHITE : COLOR_WHITE + 10;
|
|
|
|
|
- case ConsoleColor.DarkGray:
|
|
|
|
|
- return isForeground ? COLOR_BRIGHT_BLACK : COLOR_BRIGHT_BLACK + 10;
|
|
|
|
|
- case ConsoleColor.Blue:
|
|
|
|
|
- return isForeground ? COLOR_BRIGHT_BLUE : COLOR_BRIGHT_BLUE + 10;
|
|
|
|
|
- case ConsoleColor.Green:
|
|
|
|
|
- return isForeground ? COLOR_BRIGHT_GREEN : COLOR_BRIGHT_GREEN + 10;
|
|
|
|
|
- case ConsoleColor.Cyan:
|
|
|
|
|
- return isForeground ? COLOR_BRIGHT_CYAN : COLOR_BRIGHT_CYAN + 10;
|
|
|
|
|
- case ConsoleColor.Red:
|
|
|
|
|
- return isForeground ? COLOR_BRIGHT_RED : COLOR_BRIGHT_RED + 10;
|
|
|
|
|
- case ConsoleColor.Magenta:
|
|
|
|
|
- return isForeground ? COLOR_BRIGHT_MAGENTA : COLOR_BRIGHT_MAGENTA + 10;
|
|
|
|
|
- case ConsoleColor.Yellow:
|
|
|
|
|
- return isForeground ? COLOR_BRIGHT_YELLOW : COLOR_BRIGHT_YELLOW + 10;
|
|
|
|
|
- case ConsoleColor.White:
|
|
|
|
|
- return isForeground ? COLOR_BRIGHT_WHITE : COLOR_BRIGHT_WHITE + 10;
|
|
|
|
|
- }
|
|
|
|
|
- return 0;
|
|
|
|
|
|
|
+ public virtual void ResizeScreen ()
|
|
|
|
|
+ {
|
|
|
|
|
+ if (NetWinConsole == null) {
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- bool SetCursorPosition (int col, int row)
|
|
|
|
|
- {
|
|
|
|
|
|
|
+ if (Console.WindowHeight > 0) {
|
|
|
|
|
+ // Not supported on Unix.
|
|
|
if (IsWinPlatform) {
|
|
if (IsWinPlatform) {
|
|
|
- // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth.
|
|
|
|
|
|
|
+ // Can raise an exception while is still resizing.
|
|
|
try {
|
|
try {
|
|
|
- Console.SetCursorPosition (col, row);
|
|
|
|
|
- return true;
|
|
|
|
|
- } catch (Exception) {
|
|
|
|
|
- return false;
|
|
|
|
|
|
|
+#pragma warning disable CA1416
|
|
|
|
|
+ Console.CursorTop = 0;
|
|
|
|
|
+ Console.CursorLeft = 0;
|
|
|
|
|
+ Console.WindowTop = 0;
|
|
|
|
|
+ Console.WindowLeft = 0;
|
|
|
|
|
+ if (Console.WindowHeight > Rows) {
|
|
|
|
|
+ Console.SetWindowSize (Cols, Rows);
|
|
|
|
|
+ }
|
|
|
|
|
+ Console.SetBufferSize (Cols, Rows);
|
|
|
|
|
+#pragma warning restore CA1416
|
|
|
|
|
+ } catch (System.IO.IOException) {
|
|
|
|
|
+ Clip = new Rect (0, 0, Cols, Rows);
|
|
|
|
|
+ } catch (ArgumentOutOfRangeException) {
|
|
|
|
|
+ Clip = new Rect (0, 0, Cols, Rows);
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
|
- SetVirtualCursorPosition (col, row);
|
|
|
|
|
- return true;
|
|
|
|
|
|
|
+ Console.Out.Write (EscSeqUtils.CSI_SetTerminalWindowSize (Rows, Cols));
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private void SetWindowPosition (int col, int row)
|
|
|
|
|
- {
|
|
|
|
|
- if (IsWinPlatform && EnableConsoleScrolling) {
|
|
|
|
|
- var winTop = Math.Max (Rows - Console.WindowHeight - row, 0);
|
|
|
|
|
- winTop = Math.Min (winTop, Rows - Console.WindowHeight + 1);
|
|
|
|
|
- winTop = Math.Max (winTop, 0);
|
|
|
|
|
- if (winTop != Console.WindowTop) {
|
|
|
|
|
- try {
|
|
|
|
|
- if (!EnsureBufferSize ()) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-#pragma warning disable CA1416
|
|
|
|
|
- Console.SetWindowPosition (col, winTop);
|
|
|
|
|
-#pragma warning restore CA1416
|
|
|
|
|
- } catch (System.IO.IOException) {
|
|
|
|
|
|
|
+ Clip = new Rect (0, 0, Cols, Rows);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- } catch (System.ArgumentOutOfRangeException) { }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- top = Console.WindowTop;
|
|
|
|
|
- left = Console.WindowLeft;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ public override void Refresh ()
|
|
|
|
|
+ {
|
|
|
|
|
+ UpdateScreen ();
|
|
|
|
|
+ UpdateCursor ();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- private bool EnsureBufferSize ()
|
|
|
|
|
- {
|
|
|
|
|
-#pragma warning disable CA1416
|
|
|
|
|
- if (IsWinPlatform && Console.BufferHeight < Rows) {
|
|
|
|
|
- try {
|
|
|
|
|
- Console.SetBufferSize (Console.WindowWidth, Rows);
|
|
|
|
|
- } catch (Exception) {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-#pragma warning restore CA1416
|
|
|
|
|
- return true;
|
|
|
|
|
|
|
+ public override void UpdateScreen ()
|
|
|
|
|
+ {
|
|
|
|
|
+ if (_winSizeChanging || Console.WindowHeight < 1 || Contents.Length != Rows * Cols || Rows != Console.WindowHeight) {
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private CursorVisibility? savedCursorVisibility;
|
|
|
|
|
|
|
+ var top = 0;
|
|
|
|
|
+ var left = 0;
|
|
|
|
|
+ var rows = Rows;
|
|
|
|
|
+ var cols = Cols;
|
|
|
|
|
+ System.Text.StringBuilder output = new System.Text.StringBuilder ();
|
|
|
|
|
+ Attribute redrawAttr = new Attribute ();
|
|
|
|
|
+ var lastCol = -1;
|
|
|
|
|
|
|
|
- public override void UpdateCursor ()
|
|
|
|
|
- {
|
|
|
|
|
- EnsureCursorVisibility ();
|
|
|
|
|
- //Debug.WriteLine ($"Before - CursorTop: {Console.CursorTop};CursorLeft: {Console.CursorLeft}");
|
|
|
|
|
|
|
+ //GetCursorVisibility (out CursorVisibility savedVisibitity);
|
|
|
|
|
+ //SetCursorVisibility (CursorVisibility.Invisible);
|
|
|
|
|
|
|
|
- if (ccol >= 0 && ccol < Cols && crow >= 0 && crow < Rows) {
|
|
|
|
|
- SetCursorPosition (ccol, crow);
|
|
|
|
|
- SetWindowPosition (0, crow);
|
|
|
|
|
|
|
+ for (var row = top; row < rows; row++) {
|
|
|
|
|
+ if (Console.WindowHeight < 1) {
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
- //Debug.WriteLine ($"WindowTop: {Console.WindowTop};WindowLeft: {Console.WindowLeft}");
|
|
|
|
|
- //Debug.WriteLine ($"After - CursorTop: {Console.CursorTop};CursorLeft: {Console.CursorLeft}");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (!_dirtyLines [row]) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!SetCursorPosition (0, row)) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ _dirtyLines [row] = false;
|
|
|
|
|
+ output.Clear ();
|
|
|
|
|
+ for (var col = left; col < cols; col++) {
|
|
|
|
|
+ lastCol = -1;
|
|
|
|
|
+ var outputWidth = 0;
|
|
|
|
|
+ for (; col < cols; col++) {
|
|
|
|
|
+ if (!Contents [row, col].IsDirty) {
|
|
|
|
|
+ if (output.Length > 0) {
|
|
|
|
|
+ WriteToConsole (output, ref lastCol, row, ref outputWidth);
|
|
|
|
|
+ } else if (lastCol == -1) {
|
|
|
|
|
+ lastCol = col;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (lastCol + 1 < cols)
|
|
|
|
|
+ lastCol++;
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- public override void StartReportingMouseMoves ()
|
|
|
|
|
- {
|
|
|
|
|
- Console.Out.Write (EscSeqUtils.EnableMouseEvents);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (lastCol == -1) {
|
|
|
|
|
+ lastCol = col;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- public override void StopReportingMouseMoves ()
|
|
|
|
|
- {
|
|
|
|
|
- Console.Out.Write (EscSeqUtils.DisableMouseEvents);
|
|
|
|
|
|
|
+ Attribute attr = Contents [row, col].Attribute.Value;
|
|
|
|
|
+ // Performance: Only send the escape sequence if the attribute has changed.
|
|
|
|
|
+ if (attr != redrawAttr) {
|
|
|
|
|
+ redrawAttr = attr;
|
|
|
|
|
+ output.Append (EscSeqUtils.CSI_SetGraphicsRendition (
|
|
|
|
|
+ MapColors ((ConsoleColor)attr.Background, false), MapColors ((ConsoleColor)attr.Foreground, true)));
|
|
|
|
|
+ }
|
|
|
|
|
+ outputWidth++;
|
|
|
|
|
+ var rune = (Rune)Contents [row, col].Runes [0];
|
|
|
|
|
+ output.Append (rune.ToString ());
|
|
|
|
|
+ if (rune.IsSurrogatePair () && rune.GetColumns () < 2) {
|
|
|
|
|
+ WriteToConsole (output, ref lastCol, row, ref outputWidth);
|
|
|
|
|
+ Console.CursorLeft--;
|
|
|
|
|
+ }
|
|
|
|
|
+ Contents [row, col].IsDirty = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (output.Length > 0) {
|
|
|
|
|
+ SetCursorPosition (lastCol, row);
|
|
|
|
|
+ Console.Write (output);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
+ SetCursorPosition (0, 0);
|
|
|
|
|
|
|
|
- public override void Suspend ()
|
|
|
|
|
- {
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ //SetCursorVisibility (savedVisibitity);
|
|
|
|
|
|
|
|
- public override void SetAttribute (Attribute c)
|
|
|
|
|
|
|
+ void WriteToConsole (StringBuilder output, ref int lastCol, int row, ref int outputWidth)
|
|
|
{
|
|
{
|
|
|
- base.SetAttribute (c);
|
|
|
|
|
|
|
+ SetCursorPosition (lastCol, row);
|
|
|
|
|
+ Console.Write (output);
|
|
|
|
|
+ output.Clear ();
|
|
|
|
|
+ lastCol += outputWidth;
|
|
|
|
|
+ outputWidth = 0;
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- public ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
|
|
|
|
|
- {
|
|
|
|
|
- if (consoleKeyInfo.Key != ConsoleKey.Packet) {
|
|
|
|
|
- return consoleKeyInfo;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ #region Color Handling
|
|
|
|
|
+
|
|
|
|
|
+ // Cache the list of ConsoleColor values.
|
|
|
|
|
+ private static readonly HashSet<int> ConsoleColorValues = new HashSet<int> (
|
|
|
|
|
+ Enum.GetValues (typeof (ConsoleColor)).OfType<ConsoleColor> ().Select (c => (int)c)
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // Dictionary for mapping ConsoleColor values to the values used by System.Net.Console.
|
|
|
|
|
+ private static Dictionary<ConsoleColor, int> colorMap = new Dictionary<ConsoleColor, int> {
|
|
|
|
|
+ { ConsoleColor.Black, COLOR_BLACK },
|
|
|
|
|
+ { ConsoleColor.DarkBlue, COLOR_BLUE },
|
|
|
|
|
+ { ConsoleColor.DarkGreen, COLOR_GREEN },
|
|
|
|
|
+ { ConsoleColor.DarkCyan, COLOR_CYAN },
|
|
|
|
|
+ { ConsoleColor.DarkRed, COLOR_RED },
|
|
|
|
|
+ { ConsoleColor.DarkMagenta, COLOR_MAGENTA },
|
|
|
|
|
+ { ConsoleColor.DarkYellow, COLOR_YELLOW },
|
|
|
|
|
+ { ConsoleColor.Gray, COLOR_WHITE },
|
|
|
|
|
+ { ConsoleColor.DarkGray, COLOR_BRIGHT_BLACK },
|
|
|
|
|
+ { ConsoleColor.Blue, COLOR_BRIGHT_BLUE },
|
|
|
|
|
+ { ConsoleColor.Green, COLOR_BRIGHT_GREEN },
|
|
|
|
|
+ { ConsoleColor.Cyan, COLOR_BRIGHT_CYAN },
|
|
|
|
|
+ { ConsoleColor.Red, COLOR_BRIGHT_RED },
|
|
|
|
|
+ { ConsoleColor.Magenta, COLOR_BRIGHT_MAGENTA },
|
|
|
|
|
+ { ConsoleColor.Yellow, COLOR_BRIGHT_YELLOW },
|
|
|
|
|
+ { ConsoleColor.White, COLOR_BRIGHT_WHITE }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // Map a ConsoleColor to a platform dependent value.
|
|
|
|
|
+ int MapColors (ConsoleColor color, bool isForeground = true)
|
|
|
|
|
+ {
|
|
|
|
|
+ return colorMap.TryGetValue (color, out var colorValue) ? colorValue + (isForeground ? 0 : 10) : 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <remarks>
|
|
|
|
|
+ /// In the NetDriver, colors are encoded as an int.
|
|
|
|
|
+ /// Extracts the foreground and background colors from the encoded value.
|
|
|
|
|
+ /// Assumes a 4-bit encoded value for both foreground and background colors.
|
|
|
|
|
+ /// </remarks>
|
|
|
|
|
+ internal override void GetColors (int value, out Color foreground, out Color background)
|
|
|
|
|
+ {
|
|
|
|
|
+ // Assume a 4-bit encoded value for both foreground and background colors.
|
|
|
|
|
+ foreground = (Color)((value >> 16) & 0xF);
|
|
|
|
|
+ background = (Color)(value & 0xF);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- var mod = consoleKeyInfo.Modifiers;
|
|
|
|
|
- var shift = (mod & ConsoleModifiers.Shift) != 0;
|
|
|
|
|
- var alt = (mod & ConsoleModifiers.Alt) != 0;
|
|
|
|
|
- var control = (mod & ConsoleModifiers.Control) != 0;
|
|
|
|
|
|
|
+ /// <remarks>
|
|
|
|
|
+ /// In the NetDriver, colors are encoded as an int.
|
|
|
|
|
+ /// However, the foreground color is stored in the most significant 16 bits,
|
|
|
|
|
+ /// and the background color is stored in the least significant 16 bits.
|
|
|
|
|
+ /// </remarks>
|
|
|
|
|
+ public override Attribute MakeColor (Color foreground, Color background)
|
|
|
|
|
+ {
|
|
|
|
|
+ // Encode the colors into the int value.
|
|
|
|
|
+ return new Attribute (
|
|
|
|
|
+ value: ((((int)foreground) & 0xffff) << 16) | (((int)background) & 0xffff),
|
|
|
|
|
+ foreground: foreground,
|
|
|
|
|
+ background: background
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out uint virtualKey, out _);
|
|
|
|
|
|
|
+ #endregion
|
|
|
|
|
|
|
|
- return new ConsoleKeyInfo ((char)keyChar, (ConsoleKey)virtualKey, shift, alt, control);
|
|
|
|
|
|
|
+ #region Cursor Handling
|
|
|
|
|
+ bool SetCursorPosition (int col, int row)
|
|
|
|
|
+ {
|
|
|
|
|
+ //if (IsWinPlatform) {
|
|
|
|
|
+ // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth.
|
|
|
|
|
+ try {
|
|
|
|
|
+ Console.SetCursorPosition (col, row);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ } catch (Exception) {
|
|
|
|
|
+ return false;
|
|
|
}
|
|
}
|
|
|
|
|
+ // BUGBUG: This breaks -usc on WSL; not sure why. But commenting out fixes.
|
|
|
|
|
+ //} else {
|
|
|
|
|
+ // // TODO: Explain why + 1 is needed (and why we do this for non-Windows).
|
|
|
|
|
+ // Console.Out.Write (EscSeqUtils.CSI_SetCursorPosition (row + 1, col + 1));
|
|
|
|
|
+ // return true;
|
|
|
|
|
+ //}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- Key MapKey (ConsoleKeyInfo keyInfo)
|
|
|
|
|
- {
|
|
|
|
|
- MapKeyModifiers (keyInfo, (Key)keyInfo.Key);
|
|
|
|
|
- switch (keyInfo.Key) {
|
|
|
|
|
- case ConsoleKey.Escape:
|
|
|
|
|
- return MapKeyModifiers (keyInfo, Key.Esc);
|
|
|
|
|
- case ConsoleKey.Tab:
|
|
|
|
|
- return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab;
|
|
|
|
|
- case ConsoleKey.Home:
|
|
|
|
|
- return MapKeyModifiers (keyInfo, Key.Home);
|
|
|
|
|
- case ConsoleKey.End:
|
|
|
|
|
- return MapKeyModifiers (keyInfo, Key.End);
|
|
|
|
|
- case ConsoleKey.LeftArrow:
|
|
|
|
|
- return MapKeyModifiers (keyInfo, Key.CursorLeft);
|
|
|
|
|
- case ConsoleKey.RightArrow:
|
|
|
|
|
- return MapKeyModifiers (keyInfo, Key.CursorRight);
|
|
|
|
|
- case ConsoleKey.UpArrow:
|
|
|
|
|
- return MapKeyModifiers (keyInfo, Key.CursorUp);
|
|
|
|
|
- case ConsoleKey.DownArrow:
|
|
|
|
|
- return MapKeyModifiers (keyInfo, Key.CursorDown);
|
|
|
|
|
- case ConsoleKey.PageUp:
|
|
|
|
|
- return MapKeyModifiers (keyInfo, Key.PageUp);
|
|
|
|
|
- case ConsoleKey.PageDown:
|
|
|
|
|
- return MapKeyModifiers (keyInfo, Key.PageDown);
|
|
|
|
|
- case ConsoleKey.Enter:
|
|
|
|
|
- return MapKeyModifiers (keyInfo, Key.Enter);
|
|
|
|
|
- case ConsoleKey.Spacebar:
|
|
|
|
|
- return MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar);
|
|
|
|
|
- case ConsoleKey.Backspace:
|
|
|
|
|
- return MapKeyModifiers (keyInfo, Key.Backspace);
|
|
|
|
|
- case ConsoleKey.Delete:
|
|
|
|
|
- return MapKeyModifiers (keyInfo, Key.DeleteChar);
|
|
|
|
|
- case ConsoleKey.Insert:
|
|
|
|
|
- return MapKeyModifiers (keyInfo, Key.InsertChar);
|
|
|
|
|
-
|
|
|
|
|
- case ConsoleKey.Oem1:
|
|
|
|
|
- case ConsoleKey.Oem2:
|
|
|
|
|
- case ConsoleKey.Oem3:
|
|
|
|
|
- case ConsoleKey.Oem4:
|
|
|
|
|
- case ConsoleKey.Oem5:
|
|
|
|
|
- case ConsoleKey.Oem6:
|
|
|
|
|
- case ConsoleKey.Oem7:
|
|
|
|
|
- case ConsoleKey.Oem8:
|
|
|
|
|
- case ConsoleKey.Oem102:
|
|
|
|
|
- case ConsoleKey.OemPeriod:
|
|
|
|
|
- case ConsoleKey.OemComma:
|
|
|
|
|
- case ConsoleKey.OemPlus:
|
|
|
|
|
- case ConsoleKey.OemMinus:
|
|
|
|
|
- return (Key)((uint)keyInfo.KeyChar);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- var key = keyInfo.Key;
|
|
|
|
|
- if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
|
|
|
|
|
- var delta = key - ConsoleKey.A;
|
|
|
|
|
- if (keyInfo.Modifiers == ConsoleModifiers.Control) {
|
|
|
|
|
- return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta));
|
|
|
|
|
- }
|
|
|
|
|
- if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
|
|
|
|
|
- return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta));
|
|
|
|
|
- }
|
|
|
|
|
- if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
|
|
|
|
|
- if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) {
|
|
|
|
|
- return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta));
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return (Key)((uint)keyInfo.KeyChar);
|
|
|
|
|
- }
|
|
|
|
|
- if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
|
|
|
|
|
- var delta = key - ConsoleKey.D0;
|
|
|
|
|
- if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
|
|
|
|
|
- return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta));
|
|
|
|
|
- }
|
|
|
|
|
- if (keyInfo.Modifiers == ConsoleModifiers.Control) {
|
|
|
|
|
- return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta));
|
|
|
|
|
- }
|
|
|
|
|
- if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
|
|
|
|
|
- if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) {
|
|
|
|
|
- return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return (Key)((uint)keyInfo.KeyChar);
|
|
|
|
|
- }
|
|
|
|
|
- if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) {
|
|
|
|
|
- var delta = key - ConsoleKey.F1;
|
|
|
|
|
- if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
|
|
|
|
|
- return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta));
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ CursorVisibility? _cachedCursorVisibility;
|
|
|
|
|
|
|
|
- return (Key)((uint)Key.F1 + delta);
|
|
|
|
|
- }
|
|
|
|
|
- if (keyInfo.KeyChar != 0) {
|
|
|
|
|
- return MapKeyModifiers (keyInfo, (Key)((uint)keyInfo.KeyChar));
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ public override void UpdateCursor ()
|
|
|
|
|
+ {
|
|
|
|
|
+ EnsureCursorVisibility ();
|
|
|
|
|
|
|
|
- return (Key)(0xffffffff);
|
|
|
|
|
|
|
+ if (Col >= 0 && Col < Cols && Row >= 0 && Row < Rows) {
|
|
|
|
|
+ SetCursorPosition (Col, Row);
|
|
|
|
|
+ SetWindowPosition (0, Row);
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- KeyModifiers keyModifiers;
|
|
|
|
|
|
|
+ public override bool GetCursorVisibility (out CursorVisibility visibility)
|
|
|
|
|
+ {
|
|
|
|
|
+ visibility = _cachedCursorVisibility ?? CursorVisibility.Default;
|
|
|
|
|
+ return visibility == CursorVisibility.Default;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
|
|
|
|
|
- {
|
|
|
|
|
- if (keyModifiers == null) {
|
|
|
|
|
- keyModifiers = new KeyModifiers ();
|
|
|
|
|
- }
|
|
|
|
|
- Key keyMod = new Key ();
|
|
|
|
|
- if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) {
|
|
|
|
|
- keyMod = Key.ShiftMask;
|
|
|
|
|
- keyModifiers.Shift = true;
|
|
|
|
|
- }
|
|
|
|
|
- if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) {
|
|
|
|
|
- keyMod |= Key.CtrlMask;
|
|
|
|
|
- keyModifiers.Ctrl = true;
|
|
|
|
|
- }
|
|
|
|
|
- if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) {
|
|
|
|
|
- keyMod |= Key.AltMask;
|
|
|
|
|
- keyModifiers.Alt = true;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ public override bool SetCursorVisibility (CursorVisibility visibility)
|
|
|
|
|
+ {
|
|
|
|
|
+ _cachedCursorVisibility = visibility;
|
|
|
|
|
+ var isVisible = Console.CursorVisible = visibility == CursorVisibility.Default;
|
|
|
|
|
+ //Console.Out.Write (isVisible ? EscSeqUtils.CSI_ShowCursor : EscSeqUtils.CSI_HideCursor);
|
|
|
|
|
+ return isVisible;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return keyMod != Key.Null ? keyMod | key : key;
|
|
|
|
|
|
|
+ public override bool EnsureCursorVisibility ()
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows)) {
|
|
|
|
|
+ GetCursorVisibility (out CursorVisibility cursorVisibility);
|
|
|
|
|
+ _cachedCursorVisibility = cursorVisibility;
|
|
|
|
|
+ SetCursorVisibility (CursorVisibility.Invisible);
|
|
|
|
|
+ return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- Action<KeyEvent> keyHandler;
|
|
|
|
|
- Action<KeyEvent> keyDownHandler;
|
|
|
|
|
- Action<KeyEvent> keyUpHandler;
|
|
|
|
|
- Action<MouseEvent> mouseHandler;
|
|
|
|
|
- NetMainLoop mainLoop;
|
|
|
|
|
-
|
|
|
|
|
- public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
|
|
|
|
|
- {
|
|
|
|
|
- this.keyHandler = keyHandler;
|
|
|
|
|
- this.keyDownHandler = keyDownHandler;
|
|
|
|
|
- this.keyUpHandler = keyUpHandler;
|
|
|
|
|
- this.mouseHandler = mouseHandler;
|
|
|
|
|
-
|
|
|
|
|
- var mLoop = this.mainLoop = mainLoop.Driver as NetMainLoop;
|
|
|
|
|
|
|
+ SetCursorVisibility (_cachedCursorVisibility ?? CursorVisibility.Default);
|
|
|
|
|
+ return _cachedCursorVisibility == CursorVisibility.Default;
|
|
|
|
|
+ }
|
|
|
|
|
+ #endregion
|
|
|
|
|
|
|
|
- // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will be simulated to be called.
|
|
|
|
|
- mLoop.ProcessInput = (e) => ProcessInput (e);
|
|
|
|
|
|
|
+ #region Size and Position Handling
|
|
|
|
|
|
|
|
- // Check if terminal supports requests
|
|
|
|
|
- this.mainLoop.netEvents.EscSeqReqProc.Add ("c");
|
|
|
|
|
- Console.Out.Write ("\x1b[0c");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ void SetWindowPosition (int col, int row)
|
|
|
|
|
+ {
|
|
|
|
|
+ Top = Console.WindowTop;
|
|
|
|
|
+ Left = Console.WindowLeft;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- void ProcessInput (NetEvents.InputResult inputEvent)
|
|
|
|
|
- {
|
|
|
|
|
- switch (inputEvent.EventType) {
|
|
|
|
|
- case NetEvents.EventType.Key:
|
|
|
|
|
- ConsoleKeyInfo consoleKeyInfo = inputEvent.ConsoleKeyInfo;
|
|
|
|
|
- if (consoleKeyInfo.Key == ConsoleKey.Packet) {
|
|
|
|
|
- consoleKeyInfo = FromVKPacketToKConsoleKeyInfo (consoleKeyInfo);
|
|
|
|
|
- }
|
|
|
|
|
- keyModifiers = new KeyModifiers ();
|
|
|
|
|
- var map = MapKey (consoleKeyInfo);
|
|
|
|
|
- if (map == (Key)0xffffffff) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (map == Key.Null) {
|
|
|
|
|
- keyDownHandler (new KeyEvent (map, keyModifiers));
|
|
|
|
|
- keyUpHandler (new KeyEvent (map, keyModifiers));
|
|
|
|
|
- } else {
|
|
|
|
|
- keyDownHandler (new KeyEvent (map, keyModifiers));
|
|
|
|
|
- keyHandler (new KeyEvent (map, keyModifiers));
|
|
|
|
|
- keyUpHandler (new KeyEvent (map, keyModifiers));
|
|
|
|
|
- }
|
|
|
|
|
- break;
|
|
|
|
|
- case NetEvents.EventType.Mouse:
|
|
|
|
|
- mouseHandler (ToDriverMouse (inputEvent.MouseEvent));
|
|
|
|
|
- break;
|
|
|
|
|
- case NetEvents.EventType.WindowSize:
|
|
|
|
|
- ChangeWin (inputEvent.WindowSizeEvent.Size);
|
|
|
|
|
- break;
|
|
|
|
|
- case NetEvents.EventType.RequestResponse:
|
|
|
|
|
- Application.Top.Data = inputEvent.RequestResponseEvent.ResultTuple;
|
|
|
|
|
- break;
|
|
|
|
|
|
|
+ private bool EnsureBufferSize ()
|
|
|
|
|
+ {
|
|
|
|
|
+#pragma warning disable CA1416
|
|
|
|
|
+ if (IsWinPlatform && Console.BufferHeight < Rows) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ Console.SetBufferSize (Console.WindowWidth, Rows);
|
|
|
|
|
+ } catch (Exception) {
|
|
|
|
|
+ return false;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+#pragma warning restore CA1416
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ #endregion
|
|
|
|
|
|
|
|
- volatile bool winChanging;
|
|
|
|
|
|
|
|
|
|
- void ChangeWin (Size size)
|
|
|
|
|
- {
|
|
|
|
|
- winChanging = true;
|
|
|
|
|
- if (!EnableConsoleScrolling) {
|
|
|
|
|
- largestBufferHeight = Math.Max (size.Height, 0);
|
|
|
|
|
- } else {
|
|
|
|
|
- largestBufferHeight = Math.Max (size.Height, largestBufferHeight);
|
|
|
|
|
- }
|
|
|
|
|
- top = 0;
|
|
|
|
|
- left = 0;
|
|
|
|
|
- cols = size.Width;
|
|
|
|
|
- rows = largestBufferHeight;
|
|
|
|
|
- ResizeScreen ();
|
|
|
|
|
- UpdateOffScreen ();
|
|
|
|
|
- winChanging = false;
|
|
|
|
|
- TerminalResized?.Invoke ();
|
|
|
|
|
|
|
+ public void StartReportingMouseMoves ()
|
|
|
|
|
+ {
|
|
|
|
|
+ Console.Out.Write (EscSeqUtils.CSI_EnableMouseEvents);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void StopReportingMouseMoves ()
|
|
|
|
|
+ {
|
|
|
|
|
+ Console.Out.Write (EscSeqUtils.CSI_DisableMouseEvents);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (consoleKeyInfo.Key != ConsoleKey.Packet) {
|
|
|
|
|
+ return consoleKeyInfo;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- MouseEvent ToDriverMouse (NetEvents.MouseEvent me)
|
|
|
|
|
- {
|
|
|
|
|
- //System.Diagnostics.Debug.WriteLine ($"X: {me.Position.X}; Y: {me.Position.Y}; ButtonState: {me.ButtonState}");
|
|
|
|
|
|
|
+ var mod = consoleKeyInfo.Modifiers;
|
|
|
|
|
+ var shift = (mod & ConsoleModifiers.Shift) != 0;
|
|
|
|
|
+ var alt = (mod & ConsoleModifiers.Alt) != 0;
|
|
|
|
|
+ var control = (mod & ConsoleModifiers.Control) != 0;
|
|
|
|
|
|
|
|
- MouseFlags mouseFlag = 0;
|
|
|
|
|
|
|
+ var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out uint virtualKey, out _);
|
|
|
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.Button1Pressed) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.Button1Pressed;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.Button1Released) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.Button1Released;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.Button1Clicked) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.Button1Clicked;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.Button1DoubleClicked) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.Button1DoubleClicked;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.Button1TripleClicked) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.Button1TripleClicked;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.Button2Pressed) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.Button2Pressed;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.Button2Released) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.Button2Released;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.Button2Clicked) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.Button2Clicked;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.Button2DoubleClicked) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.Button2DoubleClicked;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.Button2TripleClicked) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.Button2TripleClicked;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.Button3Pressed) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.Button3Pressed;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.Button3Released) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.Button3Released;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.Button3Clicked) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.Button3Clicked;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.Button3DoubleClicked) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.Button3DoubleClicked;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.Button3TripleClicked) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.Button3TripleClicked;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledUp) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.WheeledUp;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledDown) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.WheeledDown;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledLeft) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.WheeledLeft;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledRight) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.WheeledRight;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.Button4Pressed) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.Button4Pressed;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.Button4Released) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.Button4Released;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.Button4Clicked) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.Button4Clicked;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.Button4DoubleClicked) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.Button4DoubleClicked;
|
|
|
|
|
- }
|
|
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.Button4TripleClicked) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.Button4TripleClicked;
|
|
|
|
|
|
|
+ return new ConsoleKeyInfo ((char)keyChar, (ConsoleKey)virtualKey, shift, alt, control);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Key MapKey (ConsoleKeyInfo keyInfo)
|
|
|
|
|
+ {
|
|
|
|
|
+ MapKeyModifiers (keyInfo, (Key)keyInfo.Key);
|
|
|
|
|
+ switch (keyInfo.Key) {
|
|
|
|
|
+ case ConsoleKey.Escape:
|
|
|
|
|
+ return MapKeyModifiers (keyInfo, Key.Esc);
|
|
|
|
|
+ case ConsoleKey.Tab:
|
|
|
|
|
+ return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab;
|
|
|
|
|
+ case ConsoleKey.Home:
|
|
|
|
|
+ return MapKeyModifiers (keyInfo, Key.Home);
|
|
|
|
|
+ case ConsoleKey.End:
|
|
|
|
|
+ return MapKeyModifiers (keyInfo, Key.End);
|
|
|
|
|
+ case ConsoleKey.LeftArrow:
|
|
|
|
|
+ return MapKeyModifiers (keyInfo, Key.CursorLeft);
|
|
|
|
|
+ case ConsoleKey.RightArrow:
|
|
|
|
|
+ return MapKeyModifiers (keyInfo, Key.CursorRight);
|
|
|
|
|
+ case ConsoleKey.UpArrow:
|
|
|
|
|
+ return MapKeyModifiers (keyInfo, Key.CursorUp);
|
|
|
|
|
+ case ConsoleKey.DownArrow:
|
|
|
|
|
+ return MapKeyModifiers (keyInfo, Key.CursorDown);
|
|
|
|
|
+ case ConsoleKey.PageUp:
|
|
|
|
|
+ return MapKeyModifiers (keyInfo, Key.PageUp);
|
|
|
|
|
+ case ConsoleKey.PageDown:
|
|
|
|
|
+ return MapKeyModifiers (keyInfo, Key.PageDown);
|
|
|
|
|
+ case ConsoleKey.Enter:
|
|
|
|
|
+ return MapKeyModifiers (keyInfo, Key.Enter);
|
|
|
|
|
+ case ConsoleKey.Spacebar:
|
|
|
|
|
+ return MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar);
|
|
|
|
|
+ case ConsoleKey.Backspace:
|
|
|
|
|
+ return MapKeyModifiers (keyInfo, Key.Backspace);
|
|
|
|
|
+ case ConsoleKey.Delete:
|
|
|
|
|
+ return MapKeyModifiers (keyInfo, Key.DeleteChar);
|
|
|
|
|
+ case ConsoleKey.Insert:
|
|
|
|
|
+ return MapKeyModifiers (keyInfo, Key.InsertChar);
|
|
|
|
|
+
|
|
|
|
|
+ case ConsoleKey.Oem1:
|
|
|
|
|
+ case ConsoleKey.Oem2:
|
|
|
|
|
+ case ConsoleKey.Oem3:
|
|
|
|
|
+ case ConsoleKey.Oem4:
|
|
|
|
|
+ case ConsoleKey.Oem5:
|
|
|
|
|
+ case ConsoleKey.Oem6:
|
|
|
|
|
+ case ConsoleKey.Oem7:
|
|
|
|
|
+ case ConsoleKey.Oem8:
|
|
|
|
|
+ case ConsoleKey.Oem102:
|
|
|
|
|
+ case ConsoleKey.OemPeriod:
|
|
|
|
|
+ case ConsoleKey.OemComma:
|
|
|
|
|
+ case ConsoleKey.OemPlus:
|
|
|
|
|
+ case ConsoleKey.OemMinus:
|
|
|
|
|
+ return (Key)((uint)keyInfo.KeyChar);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var key = keyInfo.Key;
|
|
|
|
|
+ if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
|
|
|
|
|
+ var delta = key - ConsoleKey.A;
|
|
|
|
|
+ if (keyInfo.Modifiers == ConsoleModifiers.Control) {
|
|
|
|
|
+ return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
|
|
|
|
|
+ return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta));
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
|
|
|
|
|
+ if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) {
|
|
|
|
|
+ return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta));
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.ReportMousePosition) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.ReportMousePosition;
|
|
|
|
|
|
|
+ return (Key)((uint)keyInfo.KeyChar);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
|
|
|
|
|
+ var delta = key - ConsoleKey.D0;
|
|
|
|
|
+ if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
|
|
|
|
|
+ return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta));
|
|
|
}
|
|
}
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.ButtonShift) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.ButtonShift;
|
|
|
|
|
|
|
+ if (keyInfo.Modifiers == ConsoleModifiers.Control) {
|
|
|
|
|
+ return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta));
|
|
|
}
|
|
}
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.ButtonCtrl) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.ButtonCtrl;
|
|
|
|
|
|
|
+ if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
|
|
|
|
|
+ if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) {
|
|
|
|
|
+ return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- if ((me.ButtonState & NetEvents.MouseButtonState.ButtonAlt) != 0) {
|
|
|
|
|
- mouseFlag |= MouseFlags.ButtonAlt;
|
|
|
|
|
|
|
+ return (Key)((uint)keyInfo.KeyChar);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (key is >= ConsoleKey.F1 and <= ConsoleKey.F12) {
|
|
|
|
|
+ var delta = key - ConsoleKey.F1;
|
|
|
|
|
+ if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
|
|
|
|
|
+ return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return new MouseEvent () {
|
|
|
|
|
- X = me.Position.X,
|
|
|
|
|
- Y = me.Position.Y,
|
|
|
|
|
- Flags = mouseFlag
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ return (Key)((uint)Key.F1 + delta);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- /// <inheritdoc/>
|
|
|
|
|
- public override bool GetCursorVisibility (out CursorVisibility visibility)
|
|
|
|
|
- {
|
|
|
|
|
- visibility = savedCursorVisibility ?? CursorVisibility.Default;
|
|
|
|
|
- return visibility == CursorVisibility.Default;
|
|
|
|
|
|
|
+ if (keyInfo.KeyChar != 0) {
|
|
|
|
|
+ return MapKeyModifiers (keyInfo, (Key)((uint)keyInfo.KeyChar));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /// <inheritdoc/>
|
|
|
|
|
- public override bool SetCursorVisibility (CursorVisibility visibility)
|
|
|
|
|
- {
|
|
|
|
|
- savedCursorVisibility = visibility;
|
|
|
|
|
- var isVisible = Console.CursorVisible = visibility == CursorVisibility.Default;
|
|
|
|
|
- if (isVisible) {
|
|
|
|
|
- Console.Out.Write ("\x1b[?25h");
|
|
|
|
|
- } else {
|
|
|
|
|
- Console.Out.Write ("\x1b[?25l");
|
|
|
|
|
- }
|
|
|
|
|
- return isVisible;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ return (Key)(0xffffffff);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- /// <inheritdoc/>
|
|
|
|
|
- public override bool EnsureCursorVisibility ()
|
|
|
|
|
- {
|
|
|
|
|
- if (!(ccol >= 0 && crow >= 0 && ccol < Cols && crow < Rows)) {
|
|
|
|
|
- GetCursorVisibility (out CursorVisibility cursorVisibility);
|
|
|
|
|
- savedCursorVisibility = cursorVisibility;
|
|
|
|
|
- SetCursorVisibility (CursorVisibility.Invisible);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ KeyModifiers _keyModifiers;
|
|
|
|
|
|
|
|
- SetCursorVisibility (savedCursorVisibility ?? CursorVisibility.Default);
|
|
|
|
|
- return savedCursorVisibility == CursorVisibility.Default;
|
|
|
|
|
|
|
+ Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
|
|
|
|
|
+ {
|
|
|
|
|
+ _keyModifiers ??= new KeyModifiers ();
|
|
|
|
|
+ Key keyMod = new Key ();
|
|
|
|
|
+ if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) {
|
|
|
|
|
+ keyMod = Key.ShiftMask;
|
|
|
|
|
+ _keyModifiers.Shift = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) {
|
|
|
|
|
+ keyMod |= Key.CtrlMask;
|
|
|
|
|
+ _keyModifiers.Ctrl = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) {
|
|
|
|
|
+ keyMod |= Key.AltMask;
|
|
|
|
|
+ _keyModifiers.Alt = true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control)
|
|
|
|
|
- {
|
|
|
|
|
- NetEvents.InputResult input = new NetEvents.InputResult ();
|
|
|
|
|
- input.EventType = NetEvents.EventType.Key;
|
|
|
|
|
- input.ConsoleKeyInfo = new ConsoleKeyInfo (keyChar, key, shift, alt, control);
|
|
|
|
|
|
|
+ return keyMod != Key.Null ? keyMod | key : key;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- try {
|
|
|
|
|
- ProcessInput (input);
|
|
|
|
|
- } catch (OverflowException) { }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ Action<KeyEvent> _keyHandler;
|
|
|
|
|
+ Action<KeyEvent> _keyDownHandler;
|
|
|
|
|
+ Action<KeyEvent> _keyUpHandler;
|
|
|
|
|
+ Action<MouseEvent> _mouseHandler;
|
|
|
|
|
+ NetMainLoop _mainLoop;
|
|
|
|
|
|
|
|
- public override bool GetColors (int value, out Color foreground, out Color background)
|
|
|
|
|
- {
|
|
|
|
|
- bool hasColor = false;
|
|
|
|
|
- foreground = default;
|
|
|
|
|
- background = default;
|
|
|
|
|
- IEnumerable<int> values = Enum.GetValues (typeof (ConsoleColor))
|
|
|
|
|
- .OfType<ConsoleColor> ()
|
|
|
|
|
- .Select (s => (int)s);
|
|
|
|
|
- if (values.Contains (value & 0xffff)) {
|
|
|
|
|
- hasColor = true;
|
|
|
|
|
- background = (Color)(ConsoleColor)(value & 0xffff);
|
|
|
|
|
|
|
+ public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
|
|
|
|
|
+ {
|
|
|
|
|
+ _keyHandler = keyHandler;
|
|
|
|
|
+ _keyDownHandler = keyDownHandler;
|
|
|
|
|
+ _keyUpHandler = keyUpHandler;
|
|
|
|
|
+ _mouseHandler = mouseHandler;
|
|
|
|
|
+
|
|
|
|
|
+ var mLoop = _mainLoop = mainLoop.MainLoopDriver as NetMainLoop;
|
|
|
|
|
+
|
|
|
|
|
+ // Note: .Net API doesn't support keydown/up events and thus any passed keyDown/UpHandlers will be simulated to be called.
|
|
|
|
|
+ mLoop.ProcessInput = (e) => ProcessInput (e);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ volatile bool _winSizeChanging;
|
|
|
|
|
+
|
|
|
|
|
+ void ProcessInput (NetEvents.InputResult inputEvent)
|
|
|
|
|
+ {
|
|
|
|
|
+ switch (inputEvent.EventType) {
|
|
|
|
|
+ case NetEvents.EventType.Key:
|
|
|
|
|
+ ConsoleKeyInfo consoleKeyInfo = inputEvent.ConsoleKeyInfo;
|
|
|
|
|
+ if (consoleKeyInfo.Key == ConsoleKey.Packet) {
|
|
|
|
|
+ consoleKeyInfo = FromVKPacketToKConsoleKeyInfo (consoleKeyInfo);
|
|
|
}
|
|
}
|
|
|
- if (values.Contains ((value >> 16) & 0xffff)) {
|
|
|
|
|
- hasColor = true;
|
|
|
|
|
- foreground = (Color)(ConsoleColor)((value >> 16) & 0xffff);
|
|
|
|
|
|
|
+ _keyModifiers = new KeyModifiers ();
|
|
|
|
|
+ var map = MapKey (consoleKeyInfo);
|
|
|
|
|
+ if (map == (Key)0xffffffff) {
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
- return hasColor;
|
|
|
|
|
|
|
+ if (map == Key.Null) {
|
|
|
|
|
+ _keyDownHandler (new KeyEvent (map, _keyModifiers));
|
|
|
|
|
+ _keyUpHandler (new KeyEvent (map, _keyModifiers));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ _keyDownHandler (new KeyEvent (map, _keyModifiers));
|
|
|
|
|
+ _keyHandler (new KeyEvent (map, _keyModifiers));
|
|
|
|
|
+ _keyUpHandler (new KeyEvent (map, _keyModifiers));
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ case NetEvents.EventType.Mouse:
|
|
|
|
|
+ _mouseHandler (ToDriverMouse (inputEvent.MouseEvent));
|
|
|
|
|
+ break;
|
|
|
|
|
+ case NetEvents.EventType.WindowSize:
|
|
|
|
|
+ _winSizeChanging = true;
|
|
|
|
|
+ Top = 0;
|
|
|
|
|
+ Left = 0;
|
|
|
|
|
+ Cols = inputEvent.WindowSizeEvent.Size.Width;
|
|
|
|
|
+ Rows = Math.Max (inputEvent.WindowSizeEvent.Size.Height, 0); ;
|
|
|
|
|
+ ResizeScreen ();
|
|
|
|
|
+ ClearContents ();
|
|
|
|
|
+ _winSizeChanging = false;
|
|
|
|
|
+ TerminalResized?.Invoke ();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case NetEvents.EventType.RequestResponse:
|
|
|
|
|
+ // BUGBUG: What is this for? It does not seem to be used anywhere.
|
|
|
|
|
+ // It is also not clear what it does. View.Data is documented as "This property is not used internally"
|
|
|
|
|
+ Application.Top.Data = inputEvent.RequestResponseEvent.ResultTuple;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case NetEvents.EventType.WindowPosition:
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ throw new ArgumentOutOfRangeException ();
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- #region Unused
|
|
|
|
|
- public override void SetColors (ConsoleColor foreground, ConsoleColor background)
|
|
|
|
|
- {
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ MouseEvent ToDriverMouse (NetEvents.MouseEvent me)
|
|
|
|
|
+ {
|
|
|
|
|
+ //System.Diagnostics.Debug.WriteLine ($"X: {me.Position.X}; Y: {me.Position.Y}; ButtonState: {me.ButtonState}");
|
|
|
|
|
|
|
|
- public override void SetColors (short foregroundColorId, short backgroundColorId)
|
|
|
|
|
- {
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ MouseFlags mouseFlag = 0;
|
|
|
|
|
|
|
|
- public override void CookMouse ()
|
|
|
|
|
- {
|
|
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button1Pressed) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.Button1Pressed;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- public override void UncookMouse ()
|
|
|
|
|
- {
|
|
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button1Released) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.Button1Released;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button1Clicked) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.Button1Clicked;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button1DoubleClicked) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.Button1DoubleClicked;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button1TripleClicked) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.Button1TripleClicked;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button2Pressed) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.Button2Pressed;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button2Released) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.Button2Released;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button2Clicked) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.Button2Clicked;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button2DoubleClicked) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.Button2DoubleClicked;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button2TripleClicked) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.Button2TripleClicked;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button3Pressed) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.Button3Pressed;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button3Released) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.Button3Released;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button3Clicked) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.Button3Clicked;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button3DoubleClicked) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.Button3DoubleClicked;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button3TripleClicked) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.Button3TripleClicked;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledUp) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.WheeledUp;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledDown) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.WheeledDown;
|
|
|
}
|
|
}
|
|
|
- #endregion
|
|
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledLeft) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.WheeledLeft;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledRight) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.WheeledRight;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button4Pressed) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.Button4Pressed;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button4Released) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.Button4Released;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button4Clicked) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.Button4Clicked;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button4DoubleClicked) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.Button4DoubleClicked;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button4TripleClicked) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.Button4TripleClicked;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.ReportMousePosition) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.ReportMousePosition;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.ButtonShift) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.ButtonShift;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.ButtonCtrl) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.ButtonCtrl;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((me.ButtonState & NetEvents.MouseButtonState.ButtonAlt) != 0) {
|
|
|
|
|
+ mouseFlag |= MouseFlags.ButtonAlt;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return new MouseEvent () {
|
|
|
|
|
+ X = me.Position.X,
|
|
|
|
|
+ Y = me.Position.Y,
|
|
|
|
|
+ Flags = mouseFlag
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control)
|
|
|
|
|
+ {
|
|
|
|
|
+ NetEvents.InputResult input = new NetEvents.InputResult {
|
|
|
|
|
+ EventType = NetEvents.EventType.Key,
|
|
|
|
|
+ ConsoleKeyInfo = new ConsoleKeyInfo (keyChar, key, shift, alt, control)
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ ProcessInput (input);
|
|
|
|
|
+ } catch (OverflowException) { }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- //
|
|
|
|
|
- // These are for the .NET driver, but running natively on Windows, wont run
|
|
|
|
|
- // on the Mono emulation
|
|
|
|
|
- //
|
|
|
|
|
|
|
|
|
|
|
|
+ #region Not Implemented
|
|
|
|
|
+ public override void Suspend ()
|
|
|
|
|
+ {
|
|
|
|
|
+ throw new NotImplementedException ();
|
|
|
}
|
|
}
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// <summary>
|
|
|
|
|
+/// Mainloop intended to be used with the .NET System.Console API, and can
|
|
|
|
|
+/// be used on Windows and Unix, it is cross platform but lacks things like
|
|
|
|
|
+/// file descriptor monitoring.
|
|
|
|
|
+/// </summary>
|
|
|
|
|
+/// <remarks>
|
|
|
|
|
+/// This implementation is used for NetDriver.
|
|
|
|
|
+/// </remarks>
|
|
|
|
|
+internal class NetMainLoop : IMainLoopDriver {
|
|
|
|
|
+ ManualResetEventSlim _keyReady = new ManualResetEventSlim (false);
|
|
|
|
|
+ ManualResetEventSlim _waitForProbe = new ManualResetEventSlim (false);
|
|
|
|
|
+ Queue<NetEvents.InputResult?> _inputResult = new Queue<NetEvents.InputResult?> ();
|
|
|
|
|
+ MainLoop _mainLoop;
|
|
|
|
|
+ CancellationTokenSource _tokenSource = new CancellationTokenSource ();
|
|
|
|
|
+ internal NetEvents _netEvents;
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
- /// Mainloop intended to be used with the .NET System.Console API, and can
|
|
|
|
|
- /// be used on Windows and Unix, it is cross platform but lacks things like
|
|
|
|
|
- /// file descriptor monitoring.
|
|
|
|
|
|
|
+ /// Invoked when a Key is pressed.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ internal Action<NetEvents.InputResult> ProcessInput;
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Initializes the class with the console driver.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
/// <remarks>
|
|
/// <remarks>
|
|
|
- /// This implementation is used for NetDriver.
|
|
|
|
|
|
|
+ /// Passing a consoleDriver is provided to capture windows resizing.
|
|
|
/// </remarks>
|
|
/// </remarks>
|
|
|
- internal class NetMainLoop : IMainLoopDriver {
|
|
|
|
|
- ManualResetEventSlim keyReady = new ManualResetEventSlim (false);
|
|
|
|
|
- ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
|
|
|
|
|
- Queue<NetEvents.InputResult?> inputResult = new Queue<NetEvents.InputResult?> ();
|
|
|
|
|
- MainLoop mainLoop;
|
|
|
|
|
- CancellationTokenSource tokenSource = new CancellationTokenSource ();
|
|
|
|
|
- internal NetEvents netEvents;
|
|
|
|
|
-
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Invoked when a Key is pressed.
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- public Action<NetEvents.InputResult> ProcessInput;
|
|
|
|
|
-
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Initializes the class with the console driver.
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- /// <remarks>
|
|
|
|
|
- /// Passing a consoleDriver is provided to capture windows resizing.
|
|
|
|
|
- /// </remarks>
|
|
|
|
|
- /// <param name="consoleDriver">The console driver used by this Net main loop.</param>
|
|
|
|
|
- public NetMainLoop (ConsoleDriver consoleDriver = null)
|
|
|
|
|
- {
|
|
|
|
|
- if (consoleDriver == null) {
|
|
|
|
|
- throw new ArgumentNullException ("Console driver instance must be provided.");
|
|
|
|
|
- }
|
|
|
|
|
- netEvents = new NetEvents (consoleDriver);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ /// <param name="consoleDriver">The console driver used by this Net main loop.</param>
|
|
|
|
|
+ /// <exception cref="ArgumentNullException"></exception>
|
|
|
|
|
+ public NetMainLoop (ConsoleDriver consoleDriver = null)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (consoleDriver == null) {
|
|
|
|
|
+ throw new ArgumentNullException (nameof (consoleDriver));
|
|
|
|
|
+ }
|
|
|
|
|
+ _netEvents = new NetEvents (consoleDriver);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- void NetInputHandler ()
|
|
|
|
|
- {
|
|
|
|
|
- while (true) {
|
|
|
|
|
- waitForProbe.Wait ();
|
|
|
|
|
- waitForProbe.Reset ();
|
|
|
|
|
- if (inputResult.Count == 0) {
|
|
|
|
|
- inputResult.Enqueue (netEvents.ReadConsoleInput ());
|
|
|
|
|
- }
|
|
|
|
|
- try {
|
|
|
|
|
- while (inputResult.Peek () == null) {
|
|
|
|
|
- inputResult.Dequeue ();
|
|
|
|
|
- }
|
|
|
|
|
- if (inputResult.Count > 0) {
|
|
|
|
|
- keyReady.Set ();
|
|
|
|
|
- }
|
|
|
|
|
- } catch (InvalidOperationException) { }
|
|
|
|
|
|
|
+ void NetInputHandler ()
|
|
|
|
|
+ {
|
|
|
|
|
+ while (true) {
|
|
|
|
|
+ _waitForProbe.Wait ();
|
|
|
|
|
+ _waitForProbe.Reset ();
|
|
|
|
|
+ if (_inputResult.Count == 0) {
|
|
|
|
|
+ _inputResult.Enqueue (_netEvents.ReadConsoleInput ());
|
|
|
}
|
|
}
|
|
|
|
|
+ try {
|
|
|
|
|
+ while (_inputResult.Peek () == null) {
|
|
|
|
|
+ _inputResult.Dequeue ();
|
|
|
|
|
+ }
|
|
|
|
|
+ if (_inputResult.Count > 0) {
|
|
|
|
|
+ _keyReady.Set ();
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (InvalidOperationException) { }
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- void IMainLoopDriver.Setup (MainLoop mainLoop)
|
|
|
|
|
- {
|
|
|
|
|
- this.mainLoop = mainLoop;
|
|
|
|
|
- Task.Run (NetInputHandler);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- void IMainLoopDriver.Wakeup ()
|
|
|
|
|
- {
|
|
|
|
|
- keyReady.Set ();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ void IMainLoopDriver.Setup (MainLoop mainLoop)
|
|
|
|
|
+ {
|
|
|
|
|
+ _mainLoop = mainLoop;
|
|
|
|
|
+ Task.Run (NetInputHandler);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- bool IMainLoopDriver.EventsPending (bool wait)
|
|
|
|
|
- {
|
|
|
|
|
- waitForProbe.Set ();
|
|
|
|
|
|
|
+ void IMainLoopDriver.Wakeup ()
|
|
|
|
|
+ {
|
|
|
|
|
+ _keyReady.Set ();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (CheckTimers (wait, out var waitTimeout)) {
|
|
|
|
|
- return true;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ bool IMainLoopDriver.EventsPending (bool wait)
|
|
|
|
|
+ {
|
|
|
|
|
+ _waitForProbe.Set ();
|
|
|
|
|
|
|
|
- try {
|
|
|
|
|
- if (!tokenSource.IsCancellationRequested) {
|
|
|
|
|
- keyReady.Wait (waitTimeout, tokenSource.Token);
|
|
|
|
|
- }
|
|
|
|
|
- } catch (OperationCanceledException) {
|
|
|
|
|
- return true;
|
|
|
|
|
- } finally {
|
|
|
|
|
- keyReady.Reset ();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (CheckTimers (wait, out var waitTimeout)) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (!tokenSource.IsCancellationRequested) {
|
|
|
|
|
- return inputResult.Count > 0 || CheckTimers (wait, out _);
|
|
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (!_tokenSource.IsCancellationRequested) {
|
|
|
|
|
+ _keyReady.Wait (waitTimeout, _tokenSource.Token);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- tokenSource.Dispose ();
|
|
|
|
|
- tokenSource = new CancellationTokenSource ();
|
|
|
|
|
|
|
+ } catch (OperationCanceledException) {
|
|
|
return true;
|
|
return true;
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ _keyReady.Reset ();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- bool CheckTimers (bool wait, out int waitTimeout)
|
|
|
|
|
- {
|
|
|
|
|
- long now = DateTime.UtcNow.Ticks;
|
|
|
|
|
|
|
+ if (!_tokenSource.IsCancellationRequested) {
|
|
|
|
|
+ return _inputResult.Count > 0 || CheckTimers (wait, out _);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (mainLoop.timeouts.Count > 0) {
|
|
|
|
|
- waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond);
|
|
|
|
|
- if (waitTimeout < 0)
|
|
|
|
|
- return true;
|
|
|
|
|
- } else {
|
|
|
|
|
- waitTimeout = -1;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ _tokenSource.Dispose ();
|
|
|
|
|
+ _tokenSource = new CancellationTokenSource ();
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (!wait)
|
|
|
|
|
- waitTimeout = 0;
|
|
|
|
|
|
|
+ bool CheckTimers (bool wait, out int waitTimeout)
|
|
|
|
|
+ {
|
|
|
|
|
+ var now = DateTime.UtcNow.Ticks;
|
|
|
|
|
|
|
|
- int ic;
|
|
|
|
|
- lock (mainLoop.idleHandlers) {
|
|
|
|
|
- ic = mainLoop.idleHandlers.Count;
|
|
|
|
|
|
|
+ if (_mainLoop.timeouts.Count > 0) {
|
|
|
|
|
+ waitTimeout = (int)((_mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond);
|
|
|
|
|
+ if (waitTimeout < 0) {
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
|
|
+ } else {
|
|
|
|
|
+ waitTimeout = -1;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return ic > 0;
|
|
|
|
|
|
|
+ if (!wait) {
|
|
|
|
|
+ waitTimeout = 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- void IMainLoopDriver.Iteration ()
|
|
|
|
|
- {
|
|
|
|
|
- while (inputResult.Count > 0) {
|
|
|
|
|
- ProcessInput?.Invoke (inputResult.Dequeue ().Value);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ int ic;
|
|
|
|
|
+ lock (_mainLoop.idleHandlers) {
|
|
|
|
|
+ ic = _mainLoop.idleHandlers.Count;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return ic > 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void IMainLoopDriver.Iteration ()
|
|
|
|
|
+ {
|
|
|
|
|
+ while (_inputResult.Count > 0) {
|
|
|
|
|
+ ProcessInput?.Invoke (_inputResult.Dequeue ().Value);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ public void TearDown ()
|
|
|
|
|
+ {
|
|
|
|
|
+ //throw new NotImplementedException ();
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|