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 { /// /// Provides a platform-independent API for managing ANSI escape sequence codes. /// public static class EscSeqUtils { /// /// Represents the escape key. /// public static readonly char KeyEsc = (char)Key.Esc; /// /// Represents the CSI (Control Sequence Introducer). /// public static readonly string KeyCSI = $"{KeyEsc}["; /// /// Represents the CSI for enable any mouse event tracking. /// public static readonly string CSI_EnableAnyEventMouse = KeyCSI + "?1003h"; /// /// Represents the CSI for enable SGR (Select Graphic Rendition). /// public static readonly string CSI_EnableSgrExtModeMouse = KeyCSI + "?1006h"; /// /// Represents the CSI for enable URXVT (Unicode Extended Virtual Terminal). /// public static readonly string CSI_EnableUrxvtExtModeMouse = KeyCSI + "?1015h"; /// /// Represents the CSI for disable any mouse event tracking. /// public static readonly string CSI_DisableAnyEventMouse = KeyCSI + "?1003l"; /// /// Represents the CSI for disable SGR (Select Graphic Rendition). /// public static readonly string CSI_DisableSgrExtModeMouse = KeyCSI + "?1006l"; /// /// Represents the CSI for disable URXVT (Unicode Extended Virtual Terminal). /// public static readonly string CSI_DisableUrxvtExtModeMouse = KeyCSI + "?1015l"; /// /// Control sequence for enable mouse events. /// public static string EnableMouseEvents { get; set; } = CSI_EnableAnyEventMouse + CSI_EnableUrxvtExtModeMouse + CSI_EnableSgrExtModeMouse; /// /// Control sequence for disable mouse events. /// public static string DisableMouseEvents { get; set; } = CSI_DisableAnyEventMouse + CSI_DisableUrxvtExtModeMouse + CSI_DisableSgrExtModeMouse; /// /// Ensures a console key is mapped to one that works correctly with ANSI escape sequences. /// /// The . /// The modified. 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; } /// /// A helper to resize the as needed. /// /// The . /// The array to resize. /// The resized. 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; } /// /// Decodes a escape sequence to been processed in the appropriate manner. /// /// The which may contain a request. /// The which may changes. /// The which may changes. /// The array. /// The which may changes. /// The control returned by the method. /// The code returned by the method. /// The values returned by the method. /// The terminating returned by the method. /// Indicates if the escape sequence is a mouse key. /// The button state. /// The position. /// Indicates if the escape sequence is a response to a request. /// The handler that will process the event. 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 buttonState, out Point pos, out bool isReq, Action continuousButtonPressedHandler) { char [] kChars = GetKeyCharArray (cki); (c1Control, code, values, terminating) = GetEscapeResult (kChars); isKeyMouse = false; buttonState = new List () { 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; } } /// /// Gets all the needed information about a escape sequence. /// /// The array with all chars. /// /// The c1Control returned by , code, values and terminating. /// 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); } /// /// Gets the c1Control used in the called escape sequence. /// /// The char used. /// The c1Control. 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 } } /// /// Gets the from the value. /// /// The value. /// The or zero. 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; } } /// /// Gets the depending on terminating and value. /// /// The terminating. /// The value. /// The which may changes. /// The and probably the . 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; } /// /// A helper to get only the from the array. /// /// /// The char array of the escape sequence. 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; /// /// Gets the mouse button flags and the position. /// /// The array. /// The mouse button flags. /// The mouse position. /// The handler that will process the event. public static void GetMouse (ConsoleKeyInfo [] cki, out List mouseFlags, out Point pos, Action 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.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 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; } /// /// Get the terminal that holds the console driver. /// /// The process. /// If supported the executable console process, null otherwise. 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; } } }