|
@@ -0,0 +1,907 @@
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.Diagnostics;
|
|
|
+using System.Linq;
|
|
|
+using System.Management;
|
|
|
+using System.Runtime.InteropServices;
|
|
|
+using System.Threading.Tasks;
|
|
|
+
|
|
|
+namespace Terminal.Gui {
|
|
|
+ /// <summary>
|
|
|
+ /// Provides a platform-independent API for managing ANSI escape sequence codes.
|
|
|
+ /// </summary>
|
|
|
+ public static class EscSeqUtils {
|
|
|
+ /// <summary>
|
|
|
+ /// Represents the escape key.
|
|
|
+ /// </summary>
|
|
|
+ public static readonly char KeyEsc = (char)Key.Esc;
|
|
|
+ /// <summary>
|
|
|
+ /// Represents the CSI (Control Sequence Introducer).
|
|
|
+ /// </summary>
|
|
|
+ public static readonly string KeyCSI = $"{KeyEsc}[";
|
|
|
+ /// <summary>
|
|
|
+ /// Represents the CSI for enable any mouse event tracking.
|
|
|
+ /// </summary>
|
|
|
+ public static readonly string CSI_EnableAnyEventMouse = KeyCSI + "?1003h";
|
|
|
+ /// <summary>
|
|
|
+ /// Represents the CSI for enable SGR (Select Graphic Rendition).
|
|
|
+ /// </summary>
|
|
|
+ public static readonly string CSI_EnableSgrExtModeMouse = KeyCSI + "?1006h";
|
|
|
+ /// <summary>
|
|
|
+ /// Represents the CSI for enable URXVT (Unicode Extended Virtual Terminal).
|
|
|
+ /// </summary>
|
|
|
+ public static readonly string CSI_EnableUrxvtExtModeMouse = KeyCSI + "?1015h";
|
|
|
+ /// <summary>
|
|
|
+ /// Represents the CSI for disable any mouse event tracking.
|
|
|
+ /// </summary>
|
|
|
+ public static readonly string CSI_DisableAnyEventMouse = KeyCSI + "?1003l";
|
|
|
+ /// <summary>
|
|
|
+ /// Represents the CSI for disable SGR (Select Graphic Rendition).
|
|
|
+ /// </summary>
|
|
|
+ public static readonly string CSI_DisableSgrExtModeMouse = KeyCSI + "?1006l";
|
|
|
+ /// <summary>
|
|
|
+ /// Represents the CSI for disable URXVT (Unicode Extended Virtual Terminal).
|
|
|
+ /// </summary>
|
|
|
+ public static readonly string CSI_DisableUrxvtExtModeMouse = KeyCSI + "?1015l";
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Control sequence for enable mouse events.
|
|
|
+ /// </summary>
|
|
|
+ public static string EnableMouseEvents { get; set; } =
|
|
|
+ CSI_EnableAnyEventMouse + CSI_EnableUrxvtExtModeMouse + CSI_EnableSgrExtModeMouse;
|
|
|
+ /// <summary>
|
|
|
+ /// Control sequence for disable mouse events.
|
|
|
+ /// </summary>
|
|
|
+ public static string DisableMouseEvents { get; set; } =
|
|
|
+ CSI_DisableAnyEventMouse + CSI_DisableUrxvtExtModeMouse + CSI_DisableSgrExtModeMouse;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Ensures a console key is mapped to one that works correctly with ANSI escape sequences.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="consoleKeyInfo">The <see cref="ConsoleKeyInfo"/>.</param>
|
|
|
+ /// <returns>The <see cref="ConsoleKeyInfo"/> modified.</returns>
|
|
|
+ public static ConsoleKeyInfo GetConsoleInputKey (ConsoleKeyInfo consoleKeyInfo)
|
|
|
+ {
|
|
|
+ ConsoleKeyInfo newConsoleKeyInfo = consoleKeyInfo;
|
|
|
+ ConsoleKey key;
|
|
|
+ var keyChar = consoleKeyInfo.KeyChar;
|
|
|
+ switch ((uint)keyChar) {
|
|
|
+ case 0:
|
|
|
+ if (consoleKeyInfo.Key == (ConsoleKey)64) { // Ctrl+Space in Windows.
|
|
|
+ newConsoleKeyInfo = new ConsoleKeyInfo (' ', ConsoleKey.Spacebar,
|
|
|
+ (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
|
|
|
+ (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
|
|
|
+ (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case uint n when (n >= '\u0001' && n <= '\u001a'):
|
|
|
+ if (consoleKeyInfo.Key == 0 && consoleKeyInfo.KeyChar == '\r') {
|
|
|
+ key = ConsoleKey.Enter;
|
|
|
+ newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar,
|
|
|
+ key,
|
|
|
+ (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
|
|
|
+ (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
|
|
|
+ (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0);
|
|
|
+ } else if (consoleKeyInfo.Key == 0) {
|
|
|
+ key = (ConsoleKey)(char)(consoleKeyInfo.KeyChar + (uint)ConsoleKey.A - 1);
|
|
|
+ newConsoleKeyInfo = new ConsoleKeyInfo ((char)key,
|
|
|
+ key,
|
|
|
+ (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
|
|
|
+ (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
|
|
|
+ true);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 127:
|
|
|
+ newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar, ConsoleKey.Backspace,
|
|
|
+ (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
|
|
|
+ (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
|
|
|
+ (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ newConsoleKeyInfo = consoleKeyInfo;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return newConsoleKeyInfo;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// A helper to resize the <see cref="ConsoleKeyInfo"/> as needed.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="consoleKeyInfo">The <see cref="ConsoleKeyInfo"/>.</param>
|
|
|
+ /// <param name="cki">The <see cref="ConsoleKeyInfo"/> array to resize.</param>
|
|
|
+ /// <returns>The <see cref="ConsoleKeyInfo"/> resized.</returns>
|
|
|
+ public static ConsoleKeyInfo [] ResizeArray (ConsoleKeyInfo consoleKeyInfo, ConsoleKeyInfo [] cki)
|
|
|
+ {
|
|
|
+ Array.Resize (ref cki, cki == null ? 1 : cki.Length + 1);
|
|
|
+ cki [cki.Length - 1] = consoleKeyInfo;
|
|
|
+ return cki;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Decodes a escape sequence to been processed in the appropriate manner.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="escSeqReqProc">The <see cref="EscSeqReqProc"/> which may contain a request.</param>
|
|
|
+ /// <param name="newConsoleKeyInfo">The <see cref="ConsoleKeyInfo"/> which may changes.</param>
|
|
|
+ /// <param name="key">The <see cref="ConsoleKey"/> which may changes.</param>
|
|
|
+ /// <param name="cki">The <see cref="ConsoleKeyInfo"/> array.</param>
|
|
|
+ /// <param name="mod">The <see cref="ConsoleModifiers"/> which may changes.</param>
|
|
|
+ /// <param name="c1Control">The control returned by the <see cref="GetC1ControlChar(char)"/> method.</param>
|
|
|
+ /// <param name="code">The code returned by the <see cref="GetEscapeResult(char[])"/> method.</param>
|
|
|
+ /// <param name="values">The values returned by the <see cref="GetEscapeResult(char[])"/> method.</param>
|
|
|
+ /// <param name="terminating">The terminating returned by the <see cref="GetEscapeResult(char[])"/> method.</param>
|
|
|
+ /// <param name="isKeyMouse">Indicates if the escape sequence is a mouse key.</param>
|
|
|
+ /// <param name="buttonState">The <see cref="MouseFlags"/> button state.</param>
|
|
|
+ /// <param name="pos">The <see cref="MouseFlags"/> position.</param>
|
|
|
+ /// <param name="isReq">Indicates if the escape sequence is a response to a request.</param>
|
|
|
+ /// <param name="continuousButtonPressedHandler">The handler that will process the event.</param>
|
|
|
+ public static void DecodeEscSeq (EscSeqReqProc escSeqReqProc, ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod, out string c1Control, out string code, out string [] values, out string terminating, out bool isKeyMouse, out List<MouseFlags> buttonState, out Point pos, out bool isReq, Action<MouseFlags, Point> continuousButtonPressedHandler)
|
|
|
+ {
|
|
|
+ char [] kChars = GetKeyCharArray (cki);
|
|
|
+ (c1Control, code, values, terminating) = GetEscapeResult (kChars);
|
|
|
+ isKeyMouse = false;
|
|
|
+ buttonState = new List<MouseFlags> () { 0 };
|
|
|
+ pos = default;
|
|
|
+ isReq = false;
|
|
|
+ switch (c1Control) {
|
|
|
+ case "ESC":
|
|
|
+ if (values == null && string.IsNullOrEmpty (terminating)) {
|
|
|
+ key = ConsoleKey.Escape;
|
|
|
+ newConsoleKeyInfo = new ConsoleKeyInfo (cki [0].KeyChar, key,
|
|
|
+ (mod & ConsoleModifiers.Shift) != 0,
|
|
|
+ (mod & ConsoleModifiers.Alt) != 0,
|
|
|
+ (mod & ConsoleModifiers.Control) != 0);
|
|
|
+ } else if ((uint)cki [1].KeyChar >= 1 && (uint)cki [1].KeyChar <= 26) {
|
|
|
+ key = (ConsoleKey)(char)(cki [1].KeyChar + (uint)ConsoleKey.A - 1);
|
|
|
+ newConsoleKeyInfo = new ConsoleKeyInfo (cki [1].KeyChar,
|
|
|
+ key,
|
|
|
+ false,
|
|
|
+ true,
|
|
|
+ true);
|
|
|
+ } else {
|
|
|
+ if (cki [1].KeyChar >= 97 && cki [1].KeyChar <= 122) {
|
|
|
+ key = (ConsoleKey)cki [1].KeyChar.ToString ().ToUpper () [0];
|
|
|
+ } else {
|
|
|
+ key = (ConsoleKey)cki [1].KeyChar;
|
|
|
+ }
|
|
|
+ newConsoleKeyInfo = new ConsoleKeyInfo ((char)key,
|
|
|
+ (ConsoleKey)Math.Min ((uint)key, 255),
|
|
|
+ false,
|
|
|
+ true,
|
|
|
+ false);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case "SS3":
|
|
|
+ key = GetConsoleKey (terminating [0], values [0], ref mod);
|
|
|
+ newConsoleKeyInfo = new ConsoleKeyInfo ('\0',
|
|
|
+ key,
|
|
|
+ (mod & ConsoleModifiers.Shift) != 0,
|
|
|
+ (mod & ConsoleModifiers.Alt) != 0,
|
|
|
+ (mod & ConsoleModifiers.Control) != 0);
|
|
|
+ break;
|
|
|
+ case "CSI":
|
|
|
+ if (!string.IsNullOrEmpty (code) && code == "<") {
|
|
|
+ GetMouse (cki, out buttonState, out pos, continuousButtonPressedHandler);
|
|
|
+ isKeyMouse = true;
|
|
|
+ return;
|
|
|
+ } else if (escSeqReqProc != null && escSeqReqProc.Requested (terminating)) {
|
|
|
+ isReq = true;
|
|
|
+ escSeqReqProc.Remove (terminating);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ key = GetConsoleKey (terminating [0], values [0], ref mod);
|
|
|
+ if (key != 0 && values.Length > 1) {
|
|
|
+ mod |= GetConsoleModifiers (values [1]);
|
|
|
+ }
|
|
|
+ newConsoleKeyInfo = new ConsoleKeyInfo ('\0',
|
|
|
+ key,
|
|
|
+ (mod & ConsoleModifiers.Shift) != 0,
|
|
|
+ (mod & ConsoleModifiers.Alt) != 0,
|
|
|
+ (mod & ConsoleModifiers.Control) != 0);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets all the needed information about a escape sequence.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="kChar">The array with all chars.</param>
|
|
|
+ /// <returns>
|
|
|
+ /// The c1Control returned by <see cref="GetC1ControlChar(char)"/>, code, values and terminating.
|
|
|
+ /// </returns>
|
|
|
+ public static (string c1Control, string code, string [] values, string terminating) GetEscapeResult (char [] kChar)
|
|
|
+ {
|
|
|
+ if (kChar == null || kChar.Length == 0) {
|
|
|
+ return (null, null, null, null);
|
|
|
+ }
|
|
|
+ if (kChar [0] != '\x1b') {
|
|
|
+ throw new InvalidOperationException ("Invalid escape character!");
|
|
|
+ }
|
|
|
+ if (kChar.Length == 1) {
|
|
|
+ return ("ESC", null, null, null);
|
|
|
+ }
|
|
|
+ if (kChar.Length == 2) {
|
|
|
+ return ("ESC", null, null, kChar [1].ToString ());
|
|
|
+ }
|
|
|
+ string c1Control = GetC1ControlChar (kChar [1]);
|
|
|
+ string code = null;
|
|
|
+ int nSep = kChar.Count (x => x == ';') + 1;
|
|
|
+ string [] values = new string [nSep];
|
|
|
+ int valueIdx = 0;
|
|
|
+ string terminating = "";
|
|
|
+ for (int i = 2; i < kChar.Length; i++) {
|
|
|
+ var c = kChar [i];
|
|
|
+ if (char.IsDigit (c)) {
|
|
|
+ values [valueIdx] += c.ToString ();
|
|
|
+ } else if (c == ';') {
|
|
|
+ valueIdx++;
|
|
|
+ } else if (valueIdx == nSep - 1 || i == kChar.Length - 1) {
|
|
|
+ terminating += c.ToString ();
|
|
|
+ } else {
|
|
|
+ code += c.ToString ();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return (c1Control, code, values, terminating);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the c1Control used in the called escape sequence.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="c">The char used.</param>
|
|
|
+ /// <returns>The c1Control.</returns>
|
|
|
+ public static string GetC1ControlChar (char c)
|
|
|
+ {
|
|
|
+ // These control characters are used in the vtXXX emulation.
|
|
|
+ switch (c) {
|
|
|
+ case 'D':
|
|
|
+ return "IND"; // Index
|
|
|
+ case 'E':
|
|
|
+ return "NEL"; // Next Line
|
|
|
+ case 'H':
|
|
|
+ return "HTS"; // Tab Set
|
|
|
+ case 'M':
|
|
|
+ return "RI"; // Reverse Index
|
|
|
+ case 'N':
|
|
|
+ return "SS2"; // Single Shift Select of G2 Character Set: affects next character only
|
|
|
+ case 'O':
|
|
|
+ return "SS3"; // Single Shift Select of G3 Character Set: affects next character only
|
|
|
+ case 'P':
|
|
|
+ return "DCS"; // Device Control String
|
|
|
+ case 'V':
|
|
|
+ return "SPA"; // Start of Guarded Area
|
|
|
+ case 'W':
|
|
|
+ return "EPA"; // End of Guarded Area
|
|
|
+ case 'X':
|
|
|
+ return "SOS"; // Start of String
|
|
|
+ case 'Z':
|
|
|
+ return "DECID"; // Return Terminal ID Obsolete form of CSI c (DA)
|
|
|
+ case '[':
|
|
|
+ return "CSI"; // Control Sequence Introducer
|
|
|
+ case '\\':
|
|
|
+ return "ST"; // String Terminator
|
|
|
+ case ']':
|
|
|
+ return "OSC"; // Operating System Command
|
|
|
+ case '^':
|
|
|
+ return "PM"; // Privacy Message
|
|
|
+ case '_':
|
|
|
+ return "APC"; // Application Program Command
|
|
|
+ default:
|
|
|
+ return ""; // Not supported
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the <see cref="ConsoleModifiers"/> from the value.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="value">The value.</param>
|
|
|
+ /// <returns>The <see cref="ConsoleModifiers"/> or zero.</returns>
|
|
|
+ public static ConsoleModifiers GetConsoleModifiers (string value)
|
|
|
+ {
|
|
|
+ switch (value) {
|
|
|
+ case "2":
|
|
|
+ return ConsoleModifiers.Shift;
|
|
|
+ case "3":
|
|
|
+ return ConsoleModifiers.Alt;
|
|
|
+ case "4":
|
|
|
+ return ConsoleModifiers.Shift | ConsoleModifiers.Alt;
|
|
|
+ case "5":
|
|
|
+ return ConsoleModifiers.Control;
|
|
|
+ case "6":
|
|
|
+ return ConsoleModifiers.Shift | ConsoleModifiers.Control;
|
|
|
+ case "7":
|
|
|
+ return ConsoleModifiers.Alt | ConsoleModifiers.Control;
|
|
|
+ case "8":
|
|
|
+ return ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control;
|
|
|
+ default:
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the <see cref="ConsoleKey"/> depending on terminating and value.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="terminating">The terminating.</param>
|
|
|
+ /// <param name="value">The value.</param>
|
|
|
+ /// <param name="mod">The <see cref="ConsoleModifiers"/> which may changes.</param>
|
|
|
+ /// <returns>The <see cref="ConsoleKey"/> and probably the <see cref="ConsoleModifiers"/>.</returns>
|
|
|
+ public static ConsoleKey GetConsoleKey (char terminating, string value, ref ConsoleModifiers mod)
|
|
|
+ {
|
|
|
+ ConsoleKey key;
|
|
|
+ switch (terminating) {
|
|
|
+ case 'A':
|
|
|
+ key = ConsoleKey.UpArrow;
|
|
|
+ break;
|
|
|
+ case 'B':
|
|
|
+ key = ConsoleKey.DownArrow;
|
|
|
+ break;
|
|
|
+ case 'C':
|
|
|
+ key = ConsoleKey.RightArrow;
|
|
|
+ break;
|
|
|
+ case 'D':
|
|
|
+ key = ConsoleKey.LeftArrow;
|
|
|
+ break;
|
|
|
+ case 'F':
|
|
|
+ key = ConsoleKey.End;
|
|
|
+ break;
|
|
|
+ case 'H':
|
|
|
+ key = ConsoleKey.Home;
|
|
|
+ break;
|
|
|
+ case 'P':
|
|
|
+ key = ConsoleKey.F1;
|
|
|
+ break;
|
|
|
+ case 'Q':
|
|
|
+ key = ConsoleKey.F2;
|
|
|
+ break;
|
|
|
+ case 'R':
|
|
|
+ key = ConsoleKey.F3;
|
|
|
+ break;
|
|
|
+ case 'S':
|
|
|
+ key = ConsoleKey.F4;
|
|
|
+ break;
|
|
|
+ case 'Z':
|
|
|
+ key = ConsoleKey.Tab;
|
|
|
+ mod |= ConsoleModifiers.Shift;
|
|
|
+ break;
|
|
|
+ case '~':
|
|
|
+ switch (value) {
|
|
|
+ case "2":
|
|
|
+ key = ConsoleKey.Insert;
|
|
|
+ break;
|
|
|
+ case "3":
|
|
|
+ key = ConsoleKey.Delete;
|
|
|
+ break;
|
|
|
+ case "5":
|
|
|
+ key = ConsoleKey.PageUp;
|
|
|
+ break;
|
|
|
+ case "6":
|
|
|
+ key = ConsoleKey.PageDown;
|
|
|
+ break;
|
|
|
+ case "15":
|
|
|
+ key = ConsoleKey.F5;
|
|
|
+ break;
|
|
|
+ case "17":
|
|
|
+ key = ConsoleKey.F6;
|
|
|
+ break;
|
|
|
+ case "18":
|
|
|
+ key = ConsoleKey.F7;
|
|
|
+ break;
|
|
|
+ case "19":
|
|
|
+ key = ConsoleKey.F8;
|
|
|
+ break;
|
|
|
+ case "20":
|
|
|
+ key = ConsoleKey.F9;
|
|
|
+ break;
|
|
|
+ case "21":
|
|
|
+ key = ConsoleKey.F10;
|
|
|
+ break;
|
|
|
+ case "23":
|
|
|
+ key = ConsoleKey.F11;
|
|
|
+ break;
|
|
|
+ case "24":
|
|
|
+ key = ConsoleKey.F12;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ key = 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ key = 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return key;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// A helper to get only the <see cref="ConsoleKeyInfo.KeyChar"/> from the <see cref="ConsoleKeyInfo"/> array.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="cki"></param>
|
|
|
+ /// <returns>The char array of the escape sequence.</returns>
|
|
|
+ public static char [] GetKeyCharArray (ConsoleKeyInfo [] cki)
|
|
|
+ {
|
|
|
+ char [] kChar = new char [] { };
|
|
|
+ var length = 0;
|
|
|
+ foreach (var kc in cki) {
|
|
|
+ length++;
|
|
|
+ Array.Resize (ref kChar, length);
|
|
|
+ kChar [length - 1] = kc.KeyChar;
|
|
|
+ }
|
|
|
+
|
|
|
+ return kChar;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static MouseFlags? lastMouseButtonPressed;
|
|
|
+ //private static MouseFlags? lastMouseButtonReleased;
|
|
|
+ private static bool isButtonPressed;
|
|
|
+ //private static bool isButtonReleased;
|
|
|
+ private static bool isButtonClicked;
|
|
|
+ private static bool isButtonDoubleClicked;
|
|
|
+ private static bool isButtonTripleClicked;
|
|
|
+ private static Point point;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the <see cref="MouseFlags"/> mouse button flags and the position.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="cki">The <see cref="ConsoleKeyInfo"/> array.</param>
|
|
|
+ /// <param name="mouseFlags">The mouse button flags.</param>
|
|
|
+ /// <param name="pos">The mouse position.</param>
|
|
|
+ /// <param name="continuousButtonPressedHandler">The handler that will process the event.</param>
|
|
|
+ public static void GetMouse (ConsoleKeyInfo [] cki, out List<MouseFlags> mouseFlags, out Point pos, Action<MouseFlags, Point> continuousButtonPressedHandler)
|
|
|
+ {
|
|
|
+ MouseFlags buttonState = 0;
|
|
|
+ pos = new Point ();
|
|
|
+ int buttonCode = 0;
|
|
|
+ bool foundButtonCode = false;
|
|
|
+ int foundPoint = 0;
|
|
|
+ string value = "";
|
|
|
+ var kChar = GetKeyCharArray (cki);
|
|
|
+ //System.Diagnostics.Debug.WriteLine ($"kChar: {new string (kChar)}");
|
|
|
+ for (int i = 0; i < kChar.Length; i++) {
|
|
|
+ var c = kChar [i];
|
|
|
+ if (c == '<') {
|
|
|
+ foundButtonCode = true;
|
|
|
+ } else if (foundButtonCode && c != ';') {
|
|
|
+ value += c.ToString ();
|
|
|
+ } else if (c == ';') {
|
|
|
+ if (foundButtonCode) {
|
|
|
+ foundButtonCode = false;
|
|
|
+ buttonCode = int.Parse (value);
|
|
|
+ }
|
|
|
+ if (foundPoint == 1) {
|
|
|
+ pos.X = int.Parse (value) - 1;
|
|
|
+ }
|
|
|
+ value = "";
|
|
|
+ foundPoint++;
|
|
|
+ } else if (foundPoint > 0 && c != 'm' && c != 'M') {
|
|
|
+ value += c.ToString ();
|
|
|
+ } else if (c == 'm' || c == 'M') {
|
|
|
+ //pos.Y = int.Parse (value) + Console.WindowTop - 1;
|
|
|
+ pos.Y = int.Parse (value) - 1;
|
|
|
+
|
|
|
+ switch (buttonCode) {
|
|
|
+ case 0:
|
|
|
+ case 8:
|
|
|
+ case 16:
|
|
|
+ case 24:
|
|
|
+ case 32:
|
|
|
+ case 36:
|
|
|
+ case 40:
|
|
|
+ case 48:
|
|
|
+ case 56:
|
|
|
+ buttonState = c == 'M' ? MouseFlags.Button1Pressed
|
|
|
+ : MouseFlags.Button1Released;
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ case 9:
|
|
|
+ case 17:
|
|
|
+ case 25:
|
|
|
+ case 33:
|
|
|
+ case 37:
|
|
|
+ case 41:
|
|
|
+ case 45:
|
|
|
+ case 49:
|
|
|
+ case 53:
|
|
|
+ case 57:
|
|
|
+ case 61:
|
|
|
+ buttonState = c == 'M' ? MouseFlags.Button2Pressed
|
|
|
+ : MouseFlags.Button2Released;
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ case 10:
|
|
|
+ case 14:
|
|
|
+ case 18:
|
|
|
+ case 22:
|
|
|
+ case 26:
|
|
|
+ case 30:
|
|
|
+ case 34:
|
|
|
+ case 42:
|
|
|
+ case 46:
|
|
|
+ case 50:
|
|
|
+ case 54:
|
|
|
+ case 58:
|
|
|
+ case 62:
|
|
|
+ buttonState = c == 'M' ? MouseFlags.Button3Pressed
|
|
|
+ : MouseFlags.Button3Released;
|
|
|
+ break;
|
|
|
+ case 35:
|
|
|
+ //// Needed for Windows OS
|
|
|
+ //if (isButtonPressed && c == 'm'
|
|
|
+ // && (lastMouseEvent.ButtonState == MouseFlags.Button1Pressed
|
|
|
+ // || lastMouseEvent.ButtonState == MouseFlags.Button2Pressed
|
|
|
+ // || lastMouseEvent.ButtonState == MouseFlags.Button3Pressed)) {
|
|
|
+
|
|
|
+ // switch (lastMouseEvent.ButtonState) {
|
|
|
+ // case MouseFlags.Button1Pressed:
|
|
|
+ // buttonState = MouseFlags.Button1Released;
|
|
|
+ // break;
|
|
|
+ // case MouseFlags.Button2Pressed:
|
|
|
+ // buttonState = MouseFlags.Button2Released;
|
|
|
+ // break;
|
|
|
+ // case MouseFlags.Button3Pressed:
|
|
|
+ // buttonState = MouseFlags.Button3Released;
|
|
|
+ // break;
|
|
|
+ // }
|
|
|
+ //} else {
|
|
|
+ // buttonState = MouseFlags.ReportMousePosition;
|
|
|
+ //}
|
|
|
+ //break;
|
|
|
+ case 39:
|
|
|
+ case 43:
|
|
|
+ case 47:
|
|
|
+ case 51:
|
|
|
+ case 55:
|
|
|
+ case 59:
|
|
|
+ case 63:
|
|
|
+ buttonState = MouseFlags.ReportMousePosition;
|
|
|
+ break;
|
|
|
+ case 64:
|
|
|
+ buttonState = MouseFlags.WheeledUp;
|
|
|
+ break;
|
|
|
+ case 65:
|
|
|
+ buttonState = MouseFlags.WheeledDown;
|
|
|
+ break;
|
|
|
+ case 68:
|
|
|
+ case 72:
|
|
|
+ case 80:
|
|
|
+ buttonState = MouseFlags.WheeledLeft; // Shift/Ctrl+WheeledUp
|
|
|
+ break;
|
|
|
+ case 69:
|
|
|
+ case 73:
|
|
|
+ case 81:
|
|
|
+ buttonState = MouseFlags.WheeledRight; // Shift/Ctrl+WheeledDown
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ // Modifiers.
|
|
|
+ switch (buttonCode) {
|
|
|
+ case 8:
|
|
|
+ case 9:
|
|
|
+ case 10:
|
|
|
+ case 43:
|
|
|
+ buttonState |= MouseFlags.ButtonAlt;
|
|
|
+ break;
|
|
|
+ case 14:
|
|
|
+ case 47:
|
|
|
+ buttonState |= MouseFlags.ButtonAlt | MouseFlags.ButtonShift;
|
|
|
+ break;
|
|
|
+ case 16:
|
|
|
+ case 17:
|
|
|
+ case 18:
|
|
|
+ case 51:
|
|
|
+ buttonState |= MouseFlags.ButtonCtrl;
|
|
|
+ break;
|
|
|
+ case 22:
|
|
|
+ case 55:
|
|
|
+ buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonShift;
|
|
|
+ break;
|
|
|
+ case 24:
|
|
|
+ case 25:
|
|
|
+ case 26:
|
|
|
+ case 59:
|
|
|
+ buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonAlt;
|
|
|
+ break;
|
|
|
+ case 30:
|
|
|
+ case 63:
|
|
|
+ buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonShift | MouseFlags.ButtonAlt;
|
|
|
+ break;
|
|
|
+ case 32:
|
|
|
+ case 33:
|
|
|
+ case 34:
|
|
|
+ buttonState |= MouseFlags.ReportMousePosition;
|
|
|
+ break;
|
|
|
+ case 36:
|
|
|
+ case 37:
|
|
|
+ buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonShift;
|
|
|
+ break;
|
|
|
+ case 39:
|
|
|
+ case 68:
|
|
|
+ case 69:
|
|
|
+ buttonState |= MouseFlags.ButtonShift;
|
|
|
+ break;
|
|
|
+ case 40:
|
|
|
+ case 41:
|
|
|
+ case 42:
|
|
|
+ buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonAlt;
|
|
|
+ break;
|
|
|
+ case 45:
|
|
|
+ case 46:
|
|
|
+ buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonAlt | MouseFlags.ButtonShift;
|
|
|
+ break;
|
|
|
+ case 48:
|
|
|
+ case 49:
|
|
|
+ case 50:
|
|
|
+ buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl;
|
|
|
+ break;
|
|
|
+ case 53:
|
|
|
+ case 54:
|
|
|
+ buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonShift;
|
|
|
+ break;
|
|
|
+ case 56:
|
|
|
+ case 57:
|
|
|
+ case 58:
|
|
|
+ buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonAlt;
|
|
|
+ break;
|
|
|
+ case 61:
|
|
|
+ case 62:
|
|
|
+ buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonShift | MouseFlags.ButtonAlt;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ mouseFlags = new List<MouseFlags> () { MouseFlags.AllEvents };
|
|
|
+
|
|
|
+ if (lastMouseButtonPressed != null && !isButtonPressed && !buttonState.HasFlag (MouseFlags.ReportMousePosition)
|
|
|
+ && !buttonState.HasFlag (MouseFlags.Button1Released)
|
|
|
+ && !buttonState.HasFlag (MouseFlags.Button2Released)
|
|
|
+ && !buttonState.HasFlag (MouseFlags.Button3Released)
|
|
|
+ && !buttonState.HasFlag (MouseFlags.Button4Released)) {
|
|
|
+
|
|
|
+ lastMouseButtonPressed = null;
|
|
|
+ isButtonPressed = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!isButtonClicked && !isButtonDoubleClicked && ((buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed ||
|
|
|
+ buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed) && lastMouseButtonPressed == null) ||
|
|
|
+ isButtonPressed && lastMouseButtonPressed != null && buttonState.HasFlag (MouseFlags.ReportMousePosition)) {
|
|
|
+
|
|
|
+ mouseFlags [0] = buttonState;
|
|
|
+ lastMouseButtonPressed = buttonState;
|
|
|
+ isButtonPressed = true;
|
|
|
+
|
|
|
+ if ((mouseFlags [0] & MouseFlags.ReportMousePosition) == 0) {
|
|
|
+ point = new Point () {
|
|
|
+ X = pos.X,
|
|
|
+ Y = pos.Y
|
|
|
+ };
|
|
|
+
|
|
|
+ Application.MainLoop.AddIdle (() => {
|
|
|
+ Task.Run (async () => await ProcessContinuousButtonPressedAsync (buttonState, continuousButtonPressedHandler));
|
|
|
+ return false;
|
|
|
+ });
|
|
|
+ } else if (mouseFlags [0] == MouseFlags.ReportMousePosition) {
|
|
|
+ isButtonPressed = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if (isButtonDoubleClicked && (buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed ||
|
|
|
+ buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed)) {
|
|
|
+
|
|
|
+ mouseFlags [0] = GetButtonTripleClicked (buttonState);
|
|
|
+ isButtonDoubleClicked = false;
|
|
|
+ isButtonTripleClicked = true;
|
|
|
+
|
|
|
+ } else if (isButtonClicked && (buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed ||
|
|
|
+ buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed)) {
|
|
|
+
|
|
|
+ mouseFlags [0] = GetButtonDoubleClicked (buttonState);
|
|
|
+ isButtonClicked = false;
|
|
|
+ isButtonDoubleClicked = true;
|
|
|
+ Application.MainLoop.AddIdle (() => {
|
|
|
+ Task.Run (async () => await ProcessButtonDoubleClickedAsync ());
|
|
|
+ return false;
|
|
|
+ });
|
|
|
+
|
|
|
+ }
|
|
|
+ //else if (isButtonReleased && !isButtonClicked && buttonState == MouseFlags.ReportMousePosition) {
|
|
|
+ // mouseFlag [0] = GetButtonClicked ((MouseFlags)lastMouseButtonReleased);
|
|
|
+ // lastMouseButtonReleased = null;
|
|
|
+ // isButtonReleased = false;
|
|
|
+ // isButtonClicked = true;
|
|
|
+ // Application.MainLoop.AddIdle (() => {
|
|
|
+ // Task.Run (async () => await ProcessButtonClickedAsync ());
|
|
|
+ // return false;
|
|
|
+ // });
|
|
|
+
|
|
|
+ //}
|
|
|
+ else if (!isButtonClicked && !isButtonDoubleClicked && (buttonState == MouseFlags.Button1Released || buttonState == MouseFlags.Button2Released ||
|
|
|
+ buttonState == MouseFlags.Button3Released || buttonState == MouseFlags.Button4Released)) {
|
|
|
+
|
|
|
+ mouseFlags [0] = buttonState;
|
|
|
+ isButtonPressed = false;
|
|
|
+
|
|
|
+ if (isButtonTripleClicked) {
|
|
|
+ isButtonTripleClicked = false;
|
|
|
+ } else if (pos.X == point.X && pos.Y == point.Y) {
|
|
|
+ mouseFlags.Add (GetButtonClicked (buttonState));
|
|
|
+ isButtonClicked = true;
|
|
|
+ Application.MainLoop.AddIdle (() => {
|
|
|
+ Task.Run (async () => await ProcessButtonClickedAsync ());
|
|
|
+ return false;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ point = pos;
|
|
|
+
|
|
|
+ //if ((lastMouseButtonPressed & MouseFlags.ReportMousePosition) == 0) {
|
|
|
+ // lastMouseButtonReleased = buttonState;
|
|
|
+ // isButtonPressed = false;
|
|
|
+ // isButtonReleased = true;
|
|
|
+ //} else {
|
|
|
+ // lastMouseButtonPressed = null;
|
|
|
+ // isButtonPressed = false;
|
|
|
+ //}
|
|
|
+
|
|
|
+ } else if (buttonState == MouseFlags.WheeledUp) {
|
|
|
+
|
|
|
+ mouseFlags [0] = MouseFlags.WheeledUp;
|
|
|
+
|
|
|
+ } else if (buttonState == MouseFlags.WheeledDown) {
|
|
|
+
|
|
|
+ mouseFlags [0] = MouseFlags.WheeledDown;
|
|
|
+
|
|
|
+ } else if (buttonState == MouseFlags.WheeledLeft) {
|
|
|
+
|
|
|
+ mouseFlags [0] = MouseFlags.WheeledLeft;
|
|
|
+
|
|
|
+ } else if (buttonState == MouseFlags.WheeledRight) {
|
|
|
+
|
|
|
+ mouseFlags [0] = MouseFlags.WheeledRight;
|
|
|
+
|
|
|
+ } else if (buttonState == MouseFlags.ReportMousePosition) {
|
|
|
+ mouseFlags [0] = MouseFlags.ReportMousePosition;
|
|
|
+
|
|
|
+ } else {
|
|
|
+ mouseFlags [0] = buttonState;
|
|
|
+ //foreach (var flag in buttonState.GetUniqueFlags()) {
|
|
|
+ // mouseFlag [0] |= flag;
|
|
|
+ //}
|
|
|
+ }
|
|
|
+
|
|
|
+ mouseFlags [0] = SetControlKeyStates (buttonState, mouseFlags [0]);
|
|
|
+ //buttonState = mouseFlags;
|
|
|
+
|
|
|
+ //System.Diagnostics.Debug.WriteLine ($"buttonState: {buttonState} X: {pos.X} Y: {pos.Y}");
|
|
|
+ //foreach (var mf in mouseFlags) {
|
|
|
+ // System.Diagnostics.Debug.WriteLine ($"mouseFlags: {mf} X: {pos.X} Y: {pos.Y}");
|
|
|
+ //}
|
|
|
+ }
|
|
|
+
|
|
|
+ private static async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag, Action<MouseFlags, Point> continuousButtonPressedHandler)
|
|
|
+ {
|
|
|
+ while (isButtonPressed) {
|
|
|
+ await Task.Delay (100);
|
|
|
+ //var me = new MouseEvent () {
|
|
|
+ // X = point.X,
|
|
|
+ // Y = point.Y,
|
|
|
+ // Flags = mouseFlag
|
|
|
+ //};
|
|
|
+
|
|
|
+ var view = Application.WantContinuousButtonPressedView;
|
|
|
+ if (view == null)
|
|
|
+ break;
|
|
|
+ if (isButtonPressed && lastMouseButtonPressed != null && (mouseFlag & MouseFlags.ReportMousePosition) == 0) {
|
|
|
+ Application.MainLoop.Invoke (() => continuousButtonPressedHandler (mouseFlag, point));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static async Task ProcessButtonClickedAsync ()
|
|
|
+ {
|
|
|
+ await Task.Delay (300);
|
|
|
+ isButtonClicked = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static async Task ProcessButtonDoubleClickedAsync ()
|
|
|
+ {
|
|
|
+ await Task.Delay (300);
|
|
|
+ isButtonDoubleClicked = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static MouseFlags GetButtonClicked (MouseFlags mouseFlag)
|
|
|
+ {
|
|
|
+ MouseFlags mf = default;
|
|
|
+ switch (mouseFlag) {
|
|
|
+ case MouseFlags.Button1Released:
|
|
|
+ mf = MouseFlags.Button1Clicked;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case MouseFlags.Button2Released:
|
|
|
+ mf = MouseFlags.Button2Clicked;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case MouseFlags.Button3Released:
|
|
|
+ mf = MouseFlags.Button3Clicked;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return mf;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static MouseFlags GetButtonDoubleClicked (MouseFlags mouseFlag)
|
|
|
+ {
|
|
|
+ MouseFlags mf = default;
|
|
|
+ switch (mouseFlag) {
|
|
|
+ case MouseFlags.Button1Pressed:
|
|
|
+ mf = MouseFlags.Button1DoubleClicked;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case MouseFlags.Button2Pressed:
|
|
|
+ mf = MouseFlags.Button2DoubleClicked;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case MouseFlags.Button3Pressed:
|
|
|
+ mf = MouseFlags.Button3DoubleClicked;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return mf;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static MouseFlags GetButtonTripleClicked (MouseFlags mouseFlag)
|
|
|
+ {
|
|
|
+ MouseFlags mf = default;
|
|
|
+ switch (mouseFlag) {
|
|
|
+ case MouseFlags.Button1Pressed:
|
|
|
+ mf = MouseFlags.Button1TripleClicked;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case MouseFlags.Button2Pressed:
|
|
|
+ mf = MouseFlags.Button2TripleClicked;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case MouseFlags.Button3Pressed:
|
|
|
+ mf = MouseFlags.Button3TripleClicked;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return mf;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static MouseFlags SetControlKeyStates (MouseFlags buttonState, MouseFlags mouseFlag)
|
|
|
+ {
|
|
|
+ if ((buttonState & MouseFlags.ButtonCtrl) != 0 && (mouseFlag & MouseFlags.ButtonCtrl) == 0)
|
|
|
+ mouseFlag |= MouseFlags.ButtonCtrl;
|
|
|
+
|
|
|
+ if ((buttonState & MouseFlags.ButtonShift) != 0 && (mouseFlag & MouseFlags.ButtonShift) == 0)
|
|
|
+ mouseFlag |= MouseFlags.ButtonShift;
|
|
|
+
|
|
|
+ if ((buttonState & MouseFlags.ButtonAlt) != 0 && (mouseFlag & MouseFlags.ButtonAlt) == 0)
|
|
|
+ mouseFlag |= MouseFlags.ButtonAlt;
|
|
|
+ return mouseFlag;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Get the terminal that holds the console driver.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="process">The process.</param>
|
|
|
+ /// <returns>If supported the executable console process, null otherwise.</returns>
|
|
|
+ public static Process GetParentProcess (Process process)
|
|
|
+ {
|
|
|
+ if (!RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ string query = "SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = " + process.Id;
|
|
|
+ using (ManagementObjectSearcher mos = new ManagementObjectSearcher (query)) {
|
|
|
+ foreach (ManagementObject mo in mos.Get ()) {
|
|
|
+ if (mo ["ParentProcessId"] != null) {
|
|
|
+ try {
|
|
|
+ var id = Convert.ToInt32 (mo ["ParentProcessId"]);
|
|
|
+ return Process.GetProcessById (id);
|
|
|
+ } catch {
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|