EscSeqUtils.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.Management;
  6. using System.Runtime.InteropServices;
  7. using System.Threading.Tasks;
  8. namespace Terminal.Gui {
  9. /// <summary>
  10. /// Provides a platform-independent API for managing ANSI escape sequence codes.
  11. /// </summary>
  12. public static class EscSeqUtils {
  13. /// <summary>
  14. /// Represents the escape key.
  15. /// </summary>
  16. public static readonly char KeyEsc = (char)Key.Esc;
  17. /// <summary>
  18. /// Represents the CSI (Control Sequence Introducer).
  19. /// </summary>
  20. public static readonly string KeyCSI = $"{KeyEsc}[";
  21. /// <summary>
  22. /// Represents the CSI for enable any mouse event tracking.
  23. /// </summary>
  24. public static readonly string CSI_EnableAnyEventMouse = KeyCSI + "?1003h";
  25. /// <summary>
  26. /// Represents the CSI for enable SGR (Select Graphic Rendition).
  27. /// </summary>
  28. public static readonly string CSI_EnableSgrExtModeMouse = KeyCSI + "?1006h";
  29. /// <summary>
  30. /// Represents the CSI for enable URXVT (Unicode Extended Virtual Terminal).
  31. /// </summary>
  32. public static readonly string CSI_EnableUrxvtExtModeMouse = KeyCSI + "?1015h";
  33. /// <summary>
  34. /// Represents the CSI for disable any mouse event tracking.
  35. /// </summary>
  36. public static readonly string CSI_DisableAnyEventMouse = KeyCSI + "?1003l";
  37. /// <summary>
  38. /// Represents the CSI for disable SGR (Select Graphic Rendition).
  39. /// </summary>
  40. public static readonly string CSI_DisableSgrExtModeMouse = KeyCSI + "?1006l";
  41. /// <summary>
  42. /// Represents the CSI for disable URXVT (Unicode Extended Virtual Terminal).
  43. /// </summary>
  44. public static readonly string CSI_DisableUrxvtExtModeMouse = KeyCSI + "?1015l";
  45. /// <summary>
  46. /// Control sequence for enable mouse events.
  47. /// </summary>
  48. public static string EnableMouseEvents { get; set; } =
  49. CSI_EnableAnyEventMouse + CSI_EnableUrxvtExtModeMouse + CSI_EnableSgrExtModeMouse;
  50. /// <summary>
  51. /// Control sequence for disable mouse events.
  52. /// </summary>
  53. public static string DisableMouseEvents { get; set; } =
  54. CSI_DisableAnyEventMouse + CSI_DisableUrxvtExtModeMouse + CSI_DisableSgrExtModeMouse;
  55. /// <summary>
  56. /// Ensures a console key is mapped to one that works correctly with ANSI escape sequences.
  57. /// </summary>
  58. /// <param name="consoleKeyInfo">The <see cref="ConsoleKeyInfo"/>.</param>
  59. /// <returns>The <see cref="ConsoleKeyInfo"/> modified.</returns>
  60. public static ConsoleKeyInfo GetConsoleInputKey (ConsoleKeyInfo consoleKeyInfo)
  61. {
  62. ConsoleKeyInfo newConsoleKeyInfo = consoleKeyInfo;
  63. ConsoleKey key;
  64. var keyChar = consoleKeyInfo.KeyChar;
  65. switch ((uint)keyChar) {
  66. case 0:
  67. if (consoleKeyInfo.Key == (ConsoleKey)64) { // Ctrl+Space in Windows.
  68. newConsoleKeyInfo = new ConsoleKeyInfo (' ', ConsoleKey.Spacebar,
  69. (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
  70. (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
  71. (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0);
  72. }
  73. break;
  74. case uint n when (n >= '\u0001' && n <= '\u001a'):
  75. if (consoleKeyInfo.Key == 0 && consoleKeyInfo.KeyChar == '\r') {
  76. key = ConsoleKey.Enter;
  77. newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar,
  78. key,
  79. (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
  80. (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
  81. (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0);
  82. } else if (consoleKeyInfo.Key == 0) {
  83. key = (ConsoleKey)(char)(consoleKeyInfo.KeyChar + (uint)ConsoleKey.A - 1);
  84. newConsoleKeyInfo = new ConsoleKeyInfo ((char)key,
  85. key,
  86. (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
  87. (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
  88. true);
  89. }
  90. break;
  91. case 127:
  92. newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar, ConsoleKey.Backspace,
  93. (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
  94. (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
  95. (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0);
  96. break;
  97. default:
  98. newConsoleKeyInfo = consoleKeyInfo;
  99. break;
  100. }
  101. return newConsoleKeyInfo;
  102. }
  103. /// <summary>
  104. /// A helper to resize the <see cref="ConsoleKeyInfo"/> as needed.
  105. /// </summary>
  106. /// <param name="consoleKeyInfo">The <see cref="ConsoleKeyInfo"/>.</param>
  107. /// <param name="cki">The <see cref="ConsoleKeyInfo"/> array to resize.</param>
  108. /// <returns>The <see cref="ConsoleKeyInfo"/> resized.</returns>
  109. public static ConsoleKeyInfo [] ResizeArray (ConsoleKeyInfo consoleKeyInfo, ConsoleKeyInfo [] cki)
  110. {
  111. Array.Resize (ref cki, cki == null ? 1 : cki.Length + 1);
  112. cki [cki.Length - 1] = consoleKeyInfo;
  113. return cki;
  114. }
  115. /// <summary>
  116. /// Decodes a escape sequence to been processed in the appropriate manner.
  117. /// </summary>
  118. /// <param name="escSeqReqProc">The <see cref="EscSeqReqProc"/> which may contain a request.</param>
  119. /// <param name="newConsoleKeyInfo">The <see cref="ConsoleKeyInfo"/> which may changes.</param>
  120. /// <param name="key">The <see cref="ConsoleKey"/> which may changes.</param>
  121. /// <param name="cki">The <see cref="ConsoleKeyInfo"/> array.</param>
  122. /// <param name="mod">The <see cref="ConsoleModifiers"/> which may changes.</param>
  123. /// <param name="c1Control">The control returned by the <see cref="GetC1ControlChar(char)"/> method.</param>
  124. /// <param name="code">The code returned by the <see cref="GetEscapeResult(char[])"/> method.</param>
  125. /// <param name="values">The values returned by the <see cref="GetEscapeResult(char[])"/> method.</param>
  126. /// <param name="terminating">The terminating returned by the <see cref="GetEscapeResult(char[])"/> method.</param>
  127. /// <param name="isKeyMouse">Indicates if the escape sequence is a mouse key.</param>
  128. /// <param name="buttonState">The <see cref="MouseFlags"/> button state.</param>
  129. /// <param name="pos">The <see cref="MouseFlags"/> position.</param>
  130. /// <param name="isReq">Indicates if the escape sequence is a response to a request.</param>
  131. /// <param name="continuousButtonPressedHandler">The handler that will process the event.</param>
  132. 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)
  133. {
  134. char [] kChars = GetKeyCharArray (cki);
  135. (c1Control, code, values, terminating) = GetEscapeResult (kChars);
  136. isKeyMouse = false;
  137. buttonState = new List<MouseFlags> () { 0 };
  138. pos = default;
  139. isReq = false;
  140. switch (c1Control) {
  141. case "ESC":
  142. if (values == null && string.IsNullOrEmpty (terminating)) {
  143. key = ConsoleKey.Escape;
  144. newConsoleKeyInfo = new ConsoleKeyInfo (cki [0].KeyChar, key,
  145. (mod & ConsoleModifiers.Shift) != 0,
  146. (mod & ConsoleModifiers.Alt) != 0,
  147. (mod & ConsoleModifiers.Control) != 0);
  148. } else if ((uint)cki [1].KeyChar >= 1 && (uint)cki [1].KeyChar <= 26) {
  149. key = (ConsoleKey)(char)(cki [1].KeyChar + (uint)ConsoleKey.A - 1);
  150. newConsoleKeyInfo = new ConsoleKeyInfo (cki [1].KeyChar,
  151. key,
  152. false,
  153. true,
  154. true);
  155. } else {
  156. if (cki [1].KeyChar >= 97 && cki [1].KeyChar <= 122) {
  157. key = (ConsoleKey)cki [1].KeyChar.ToString ().ToUpper () [0];
  158. } else {
  159. key = (ConsoleKey)cki [1].KeyChar;
  160. }
  161. newConsoleKeyInfo = new ConsoleKeyInfo ((char)key,
  162. (ConsoleKey)Math.Min ((uint)key, 255),
  163. false,
  164. true,
  165. false);
  166. }
  167. break;
  168. case "SS3":
  169. key = GetConsoleKey (terminating [0], values [0], ref mod);
  170. newConsoleKeyInfo = new ConsoleKeyInfo ('\0',
  171. key,
  172. (mod & ConsoleModifiers.Shift) != 0,
  173. (mod & ConsoleModifiers.Alt) != 0,
  174. (mod & ConsoleModifiers.Control) != 0);
  175. break;
  176. case "CSI":
  177. if (!string.IsNullOrEmpty (code) && code == "<") {
  178. GetMouse (cki, out buttonState, out pos, continuousButtonPressedHandler);
  179. isKeyMouse = true;
  180. return;
  181. } else if (escSeqReqProc != null && escSeqReqProc.Requested (terminating)) {
  182. isReq = true;
  183. escSeqReqProc.Remove (terminating);
  184. return;
  185. }
  186. key = GetConsoleKey (terminating [0], values [0], ref mod);
  187. if (key != 0 && values.Length > 1) {
  188. mod |= GetConsoleModifiers (values [1]);
  189. }
  190. newConsoleKeyInfo = new ConsoleKeyInfo ('\0',
  191. key,
  192. (mod & ConsoleModifiers.Shift) != 0,
  193. (mod & ConsoleModifiers.Alt) != 0,
  194. (mod & ConsoleModifiers.Control) != 0);
  195. break;
  196. }
  197. }
  198. /// <summary>
  199. /// Gets all the needed information about a escape sequence.
  200. /// </summary>
  201. /// <param name="kChar">The array with all chars.</param>
  202. /// <returns>
  203. /// The c1Control returned by <see cref="GetC1ControlChar(char)"/>, code, values and terminating.
  204. /// </returns>
  205. public static (string c1Control, string code, string [] values, string terminating) GetEscapeResult (char [] kChar)
  206. {
  207. if (kChar == null || kChar.Length == 0) {
  208. return (null, null, null, null);
  209. }
  210. if (kChar [0] != '\x1b') {
  211. throw new InvalidOperationException ("Invalid escape character!");
  212. }
  213. if (kChar.Length == 1) {
  214. return ("ESC", null, null, null);
  215. }
  216. if (kChar.Length == 2) {
  217. return ("ESC", null, null, kChar [1].ToString ());
  218. }
  219. string c1Control = GetC1ControlChar (kChar [1]);
  220. string code = null;
  221. int nSep = kChar.Count (x => x == ';') + 1;
  222. string [] values = new string [nSep];
  223. int valueIdx = 0;
  224. string terminating = "";
  225. for (int i = 2; i < kChar.Length; i++) {
  226. var c = kChar [i];
  227. if (char.IsDigit (c)) {
  228. values [valueIdx] += c.ToString ();
  229. } else if (c == ';') {
  230. valueIdx++;
  231. } else if (valueIdx == nSep - 1 || i == kChar.Length - 1) {
  232. terminating += c.ToString ();
  233. } else {
  234. code += c.ToString ();
  235. }
  236. }
  237. return (c1Control, code, values, terminating);
  238. }
  239. /// <summary>
  240. /// Gets the c1Control used in the called escape sequence.
  241. /// </summary>
  242. /// <param name="c">The char used.</param>
  243. /// <returns>The c1Control.</returns>
  244. public static string GetC1ControlChar (char c)
  245. {
  246. // These control characters are used in the vtXXX emulation.
  247. switch (c) {
  248. case 'D':
  249. return "IND"; // Index
  250. case 'E':
  251. return "NEL"; // Next Line
  252. case 'H':
  253. return "HTS"; // Tab Set
  254. case 'M':
  255. return "RI"; // Reverse Index
  256. case 'N':
  257. return "SS2"; // Single Shift Select of G2 Character Set: affects next character only
  258. case 'O':
  259. return "SS3"; // Single Shift Select of G3 Character Set: affects next character only
  260. case 'P':
  261. return "DCS"; // Device Control String
  262. case 'V':
  263. return "SPA"; // Start of Guarded Area
  264. case 'W':
  265. return "EPA"; // End of Guarded Area
  266. case 'X':
  267. return "SOS"; // Start of String
  268. case 'Z':
  269. return "DECID"; // Return Terminal ID Obsolete form of CSI c (DA)
  270. case '[':
  271. return "CSI"; // Control Sequence Introducer
  272. case '\\':
  273. return "ST"; // String Terminator
  274. case ']':
  275. return "OSC"; // Operating System Command
  276. case '^':
  277. return "PM"; // Privacy Message
  278. case '_':
  279. return "APC"; // Application Program Command
  280. default:
  281. return ""; // Not supported
  282. }
  283. }
  284. /// <summary>
  285. /// Gets the <see cref="ConsoleModifiers"/> from the value.
  286. /// </summary>
  287. /// <param name="value">The value.</param>
  288. /// <returns>The <see cref="ConsoleModifiers"/> or zero.</returns>
  289. public static ConsoleModifiers GetConsoleModifiers (string value)
  290. {
  291. switch (value) {
  292. case "2":
  293. return ConsoleModifiers.Shift;
  294. case "3":
  295. return ConsoleModifiers.Alt;
  296. case "4":
  297. return ConsoleModifiers.Shift | ConsoleModifiers.Alt;
  298. case "5":
  299. return ConsoleModifiers.Control;
  300. case "6":
  301. return ConsoleModifiers.Shift | ConsoleModifiers.Control;
  302. case "7":
  303. return ConsoleModifiers.Alt | ConsoleModifiers.Control;
  304. case "8":
  305. return ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control;
  306. default:
  307. return 0;
  308. }
  309. }
  310. /// <summary>
  311. /// Gets the <see cref="ConsoleKey"/> depending on terminating and value.
  312. /// </summary>
  313. /// <param name="terminating">The terminating.</param>
  314. /// <param name="value">The value.</param>
  315. /// <param name="mod">The <see cref="ConsoleModifiers"/> which may changes.</param>
  316. /// <returns>The <see cref="ConsoleKey"/> and probably the <see cref="ConsoleModifiers"/>.</returns>
  317. public static ConsoleKey GetConsoleKey (char terminating, string value, ref ConsoleModifiers mod)
  318. {
  319. ConsoleKey key;
  320. switch (terminating) {
  321. case 'A':
  322. key = ConsoleKey.UpArrow;
  323. break;
  324. case 'B':
  325. key = ConsoleKey.DownArrow;
  326. break;
  327. case 'C':
  328. key = ConsoleKey.RightArrow;
  329. break;
  330. case 'D':
  331. key = ConsoleKey.LeftArrow;
  332. break;
  333. case 'F':
  334. key = ConsoleKey.End;
  335. break;
  336. case 'H':
  337. key = ConsoleKey.Home;
  338. break;
  339. case 'P':
  340. key = ConsoleKey.F1;
  341. break;
  342. case 'Q':
  343. key = ConsoleKey.F2;
  344. break;
  345. case 'R':
  346. key = ConsoleKey.F3;
  347. break;
  348. case 'S':
  349. key = ConsoleKey.F4;
  350. break;
  351. case 'Z':
  352. key = ConsoleKey.Tab;
  353. mod |= ConsoleModifiers.Shift;
  354. break;
  355. case '~':
  356. switch (value) {
  357. case "2":
  358. key = ConsoleKey.Insert;
  359. break;
  360. case "3":
  361. key = ConsoleKey.Delete;
  362. break;
  363. case "5":
  364. key = ConsoleKey.PageUp;
  365. break;
  366. case "6":
  367. key = ConsoleKey.PageDown;
  368. break;
  369. case "15":
  370. key = ConsoleKey.F5;
  371. break;
  372. case "17":
  373. key = ConsoleKey.F6;
  374. break;
  375. case "18":
  376. key = ConsoleKey.F7;
  377. break;
  378. case "19":
  379. key = ConsoleKey.F8;
  380. break;
  381. case "20":
  382. key = ConsoleKey.F9;
  383. break;
  384. case "21":
  385. key = ConsoleKey.F10;
  386. break;
  387. case "23":
  388. key = ConsoleKey.F11;
  389. break;
  390. case "24":
  391. key = ConsoleKey.F12;
  392. break;
  393. default:
  394. key = 0;
  395. break;
  396. }
  397. break;
  398. default:
  399. key = 0;
  400. break;
  401. }
  402. return key;
  403. }
  404. /// <summary>
  405. /// A helper to get only the <see cref="ConsoleKeyInfo.KeyChar"/> from the <see cref="ConsoleKeyInfo"/> array.
  406. /// </summary>
  407. /// <param name="cki"></param>
  408. /// <returns>The char array of the escape sequence.</returns>
  409. public static char [] GetKeyCharArray (ConsoleKeyInfo [] cki)
  410. {
  411. char [] kChar = new char [] { };
  412. var length = 0;
  413. foreach (var kc in cki) {
  414. length++;
  415. Array.Resize (ref kChar, length);
  416. kChar [length - 1] = kc.KeyChar;
  417. }
  418. return kChar;
  419. }
  420. private static MouseFlags? lastMouseButtonPressed;
  421. //private static MouseFlags? lastMouseButtonReleased;
  422. private static bool isButtonPressed;
  423. //private static bool isButtonReleased;
  424. private static bool isButtonClicked;
  425. private static bool isButtonDoubleClicked;
  426. private static bool isButtonTripleClicked;
  427. private static Point point;
  428. /// <summary>
  429. /// Gets the <see cref="MouseFlags"/> mouse button flags and the position.
  430. /// </summary>
  431. /// <param name="cki">The <see cref="ConsoleKeyInfo"/> array.</param>
  432. /// <param name="mouseFlags">The mouse button flags.</param>
  433. /// <param name="pos">The mouse position.</param>
  434. /// <param name="continuousButtonPressedHandler">The handler that will process the event.</param>
  435. public static void GetMouse (ConsoleKeyInfo [] cki, out List<MouseFlags> mouseFlags, out Point pos, Action<MouseFlags, Point> continuousButtonPressedHandler)
  436. {
  437. MouseFlags buttonState = 0;
  438. pos = new Point ();
  439. int buttonCode = 0;
  440. bool foundButtonCode = false;
  441. int foundPoint = 0;
  442. string value = "";
  443. var kChar = GetKeyCharArray (cki);
  444. //System.Diagnostics.Debug.WriteLine ($"kChar: {new string (kChar)}");
  445. for (int i = 0; i < kChar.Length; i++) {
  446. var c = kChar [i];
  447. if (c == '<') {
  448. foundButtonCode = true;
  449. } else if (foundButtonCode && c != ';') {
  450. value += c.ToString ();
  451. } else if (c == ';') {
  452. if (foundButtonCode) {
  453. foundButtonCode = false;
  454. buttonCode = int.Parse (value);
  455. }
  456. if (foundPoint == 1) {
  457. pos.X = int.Parse (value) - 1;
  458. }
  459. value = "";
  460. foundPoint++;
  461. } else if (foundPoint > 0 && c != 'm' && c != 'M') {
  462. value += c.ToString ();
  463. } else if (c == 'm' || c == 'M') {
  464. //pos.Y = int.Parse (value) + Console.WindowTop - 1;
  465. pos.Y = int.Parse (value) - 1;
  466. switch (buttonCode) {
  467. case 0:
  468. case 8:
  469. case 16:
  470. case 24:
  471. case 32:
  472. case 36:
  473. case 40:
  474. case 48:
  475. case 56:
  476. buttonState = c == 'M' ? MouseFlags.Button1Pressed
  477. : MouseFlags.Button1Released;
  478. break;
  479. case 1:
  480. case 9:
  481. case 17:
  482. case 25:
  483. case 33:
  484. case 37:
  485. case 41:
  486. case 45:
  487. case 49:
  488. case 53:
  489. case 57:
  490. case 61:
  491. buttonState = c == 'M' ? MouseFlags.Button2Pressed
  492. : MouseFlags.Button2Released;
  493. break;
  494. case 2:
  495. case 10:
  496. case 14:
  497. case 18:
  498. case 22:
  499. case 26:
  500. case 30:
  501. case 34:
  502. case 42:
  503. case 46:
  504. case 50:
  505. case 54:
  506. case 58:
  507. case 62:
  508. buttonState = c == 'M' ? MouseFlags.Button3Pressed
  509. : MouseFlags.Button3Released;
  510. break;
  511. case 35:
  512. //// Needed for Windows OS
  513. //if (isButtonPressed && c == 'm'
  514. // && (lastMouseEvent.ButtonState == MouseFlags.Button1Pressed
  515. // || lastMouseEvent.ButtonState == MouseFlags.Button2Pressed
  516. // || lastMouseEvent.ButtonState == MouseFlags.Button3Pressed)) {
  517. // switch (lastMouseEvent.ButtonState) {
  518. // case MouseFlags.Button1Pressed:
  519. // buttonState = MouseFlags.Button1Released;
  520. // break;
  521. // case MouseFlags.Button2Pressed:
  522. // buttonState = MouseFlags.Button2Released;
  523. // break;
  524. // case MouseFlags.Button3Pressed:
  525. // buttonState = MouseFlags.Button3Released;
  526. // break;
  527. // }
  528. //} else {
  529. // buttonState = MouseFlags.ReportMousePosition;
  530. //}
  531. //break;
  532. case 39:
  533. case 43:
  534. case 47:
  535. case 51:
  536. case 55:
  537. case 59:
  538. case 63:
  539. buttonState = MouseFlags.ReportMousePosition;
  540. break;
  541. case 64:
  542. buttonState = MouseFlags.WheeledUp;
  543. break;
  544. case 65:
  545. buttonState = MouseFlags.WheeledDown;
  546. break;
  547. case 68:
  548. case 72:
  549. case 80:
  550. buttonState = MouseFlags.WheeledLeft; // Shift/Ctrl+WheeledUp
  551. break;
  552. case 69:
  553. case 73:
  554. case 81:
  555. buttonState = MouseFlags.WheeledRight; // Shift/Ctrl+WheeledDown
  556. break;
  557. }
  558. // Modifiers.
  559. switch (buttonCode) {
  560. case 8:
  561. case 9:
  562. case 10:
  563. case 43:
  564. buttonState |= MouseFlags.ButtonAlt;
  565. break;
  566. case 14:
  567. case 47:
  568. buttonState |= MouseFlags.ButtonAlt | MouseFlags.ButtonShift;
  569. break;
  570. case 16:
  571. case 17:
  572. case 18:
  573. case 51:
  574. buttonState |= MouseFlags.ButtonCtrl;
  575. break;
  576. case 22:
  577. case 55:
  578. buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonShift;
  579. break;
  580. case 24:
  581. case 25:
  582. case 26:
  583. case 59:
  584. buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonAlt;
  585. break;
  586. case 30:
  587. case 63:
  588. buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonShift | MouseFlags.ButtonAlt;
  589. break;
  590. case 32:
  591. case 33:
  592. case 34:
  593. buttonState |= MouseFlags.ReportMousePosition;
  594. break;
  595. case 36:
  596. case 37:
  597. buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonShift;
  598. break;
  599. case 39:
  600. case 68:
  601. case 69:
  602. buttonState |= MouseFlags.ButtonShift;
  603. break;
  604. case 40:
  605. case 41:
  606. case 42:
  607. buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonAlt;
  608. break;
  609. case 45:
  610. case 46:
  611. buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonAlt | MouseFlags.ButtonShift;
  612. break;
  613. case 48:
  614. case 49:
  615. case 50:
  616. buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl;
  617. break;
  618. case 53:
  619. case 54:
  620. buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonShift;
  621. break;
  622. case 56:
  623. case 57:
  624. case 58:
  625. buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonAlt;
  626. break;
  627. case 61:
  628. case 62:
  629. buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonShift | MouseFlags.ButtonAlt;
  630. break;
  631. }
  632. }
  633. }
  634. mouseFlags = new List<MouseFlags> () { MouseFlags.AllEvents };
  635. if (lastMouseButtonPressed != null && !isButtonPressed && !buttonState.HasFlag (MouseFlags.ReportMousePosition)
  636. && !buttonState.HasFlag (MouseFlags.Button1Released)
  637. && !buttonState.HasFlag (MouseFlags.Button2Released)
  638. && !buttonState.HasFlag (MouseFlags.Button3Released)
  639. && !buttonState.HasFlag (MouseFlags.Button4Released)) {
  640. lastMouseButtonPressed = null;
  641. isButtonPressed = false;
  642. }
  643. if (!isButtonClicked && !isButtonDoubleClicked && ((buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed ||
  644. buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed) && lastMouseButtonPressed == null) ||
  645. isButtonPressed && lastMouseButtonPressed != null && buttonState.HasFlag (MouseFlags.ReportMousePosition)) {
  646. mouseFlags [0] = buttonState;
  647. lastMouseButtonPressed = buttonState;
  648. isButtonPressed = true;
  649. if ((mouseFlags [0] & MouseFlags.ReportMousePosition) == 0) {
  650. point = new Point () {
  651. X = pos.X,
  652. Y = pos.Y
  653. };
  654. Application.MainLoop.AddIdle (() => {
  655. Task.Run (async () => await ProcessContinuousButtonPressedAsync (buttonState, continuousButtonPressedHandler));
  656. return false;
  657. });
  658. } else if (mouseFlags [0] == MouseFlags.ReportMousePosition) {
  659. isButtonPressed = false;
  660. }
  661. } else if (isButtonDoubleClicked && (buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed ||
  662. buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed)) {
  663. mouseFlags [0] = GetButtonTripleClicked (buttonState);
  664. isButtonDoubleClicked = false;
  665. isButtonTripleClicked = true;
  666. } else if (isButtonClicked && (buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed ||
  667. buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed)) {
  668. mouseFlags [0] = GetButtonDoubleClicked (buttonState);
  669. isButtonClicked = false;
  670. isButtonDoubleClicked = true;
  671. Application.MainLoop.AddIdle (() => {
  672. Task.Run (async () => await ProcessButtonDoubleClickedAsync ());
  673. return false;
  674. });
  675. }
  676. //else if (isButtonReleased && !isButtonClicked && buttonState == MouseFlags.ReportMousePosition) {
  677. // mouseFlag [0] = GetButtonClicked ((MouseFlags)lastMouseButtonReleased);
  678. // lastMouseButtonReleased = null;
  679. // isButtonReleased = false;
  680. // isButtonClicked = true;
  681. // Application.MainLoop.AddIdle (() => {
  682. // Task.Run (async () => await ProcessButtonClickedAsync ());
  683. // return false;
  684. // });
  685. //}
  686. else if (!isButtonClicked && !isButtonDoubleClicked && (buttonState == MouseFlags.Button1Released || buttonState == MouseFlags.Button2Released ||
  687. buttonState == MouseFlags.Button3Released || buttonState == MouseFlags.Button4Released)) {
  688. mouseFlags [0] = buttonState;
  689. isButtonPressed = false;
  690. if (isButtonTripleClicked) {
  691. isButtonTripleClicked = false;
  692. } else if (pos.X == point.X && pos.Y == point.Y) {
  693. mouseFlags.Add (GetButtonClicked (buttonState));
  694. isButtonClicked = true;
  695. Application.MainLoop.AddIdle (() => {
  696. Task.Run (async () => await ProcessButtonClickedAsync ());
  697. return false;
  698. });
  699. }
  700. point = pos;
  701. //if ((lastMouseButtonPressed & MouseFlags.ReportMousePosition) == 0) {
  702. // lastMouseButtonReleased = buttonState;
  703. // isButtonPressed = false;
  704. // isButtonReleased = true;
  705. //} else {
  706. // lastMouseButtonPressed = null;
  707. // isButtonPressed = false;
  708. //}
  709. } else if (buttonState == MouseFlags.WheeledUp) {
  710. mouseFlags [0] = MouseFlags.WheeledUp;
  711. } else if (buttonState == MouseFlags.WheeledDown) {
  712. mouseFlags [0] = MouseFlags.WheeledDown;
  713. } else if (buttonState == MouseFlags.WheeledLeft) {
  714. mouseFlags [0] = MouseFlags.WheeledLeft;
  715. } else if (buttonState == MouseFlags.WheeledRight) {
  716. mouseFlags [0] = MouseFlags.WheeledRight;
  717. } else if (buttonState == MouseFlags.ReportMousePosition) {
  718. mouseFlags [0] = MouseFlags.ReportMousePosition;
  719. } else {
  720. mouseFlags [0] = buttonState;
  721. //foreach (var flag in buttonState.GetUniqueFlags()) {
  722. // mouseFlag [0] |= flag;
  723. //}
  724. }
  725. mouseFlags [0] = SetControlKeyStates (buttonState, mouseFlags [0]);
  726. //buttonState = mouseFlags;
  727. //System.Diagnostics.Debug.WriteLine ($"buttonState: {buttonState} X: {pos.X} Y: {pos.Y}");
  728. //foreach (var mf in mouseFlags) {
  729. // System.Diagnostics.Debug.WriteLine ($"mouseFlags: {mf} X: {pos.X} Y: {pos.Y}");
  730. //}
  731. }
  732. private static async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag, Action<MouseFlags, Point> continuousButtonPressedHandler)
  733. {
  734. while (isButtonPressed) {
  735. await Task.Delay (100);
  736. //var me = new MouseEvent () {
  737. // X = point.X,
  738. // Y = point.Y,
  739. // Flags = mouseFlag
  740. //};
  741. var view = Application.WantContinuousButtonPressedView;
  742. if (view == null)
  743. break;
  744. if (isButtonPressed && lastMouseButtonPressed != null && (mouseFlag & MouseFlags.ReportMousePosition) == 0) {
  745. Application.MainLoop.Invoke (() => continuousButtonPressedHandler (mouseFlag, point));
  746. }
  747. }
  748. }
  749. private static async Task ProcessButtonClickedAsync ()
  750. {
  751. await Task.Delay (300);
  752. isButtonClicked = false;
  753. }
  754. private static async Task ProcessButtonDoubleClickedAsync ()
  755. {
  756. await Task.Delay (300);
  757. isButtonDoubleClicked = false;
  758. }
  759. private static MouseFlags GetButtonClicked (MouseFlags mouseFlag)
  760. {
  761. MouseFlags mf = default;
  762. switch (mouseFlag) {
  763. case MouseFlags.Button1Released:
  764. mf = MouseFlags.Button1Clicked;
  765. break;
  766. case MouseFlags.Button2Released:
  767. mf = MouseFlags.Button2Clicked;
  768. break;
  769. case MouseFlags.Button3Released:
  770. mf = MouseFlags.Button3Clicked;
  771. break;
  772. }
  773. return mf;
  774. }
  775. private static MouseFlags GetButtonDoubleClicked (MouseFlags mouseFlag)
  776. {
  777. MouseFlags mf = default;
  778. switch (mouseFlag) {
  779. case MouseFlags.Button1Pressed:
  780. mf = MouseFlags.Button1DoubleClicked;
  781. break;
  782. case MouseFlags.Button2Pressed:
  783. mf = MouseFlags.Button2DoubleClicked;
  784. break;
  785. case MouseFlags.Button3Pressed:
  786. mf = MouseFlags.Button3DoubleClicked;
  787. break;
  788. }
  789. return mf;
  790. }
  791. private static MouseFlags GetButtonTripleClicked (MouseFlags mouseFlag)
  792. {
  793. MouseFlags mf = default;
  794. switch (mouseFlag) {
  795. case MouseFlags.Button1Pressed:
  796. mf = MouseFlags.Button1TripleClicked;
  797. break;
  798. case MouseFlags.Button2Pressed:
  799. mf = MouseFlags.Button2TripleClicked;
  800. break;
  801. case MouseFlags.Button3Pressed:
  802. mf = MouseFlags.Button3TripleClicked;
  803. break;
  804. }
  805. return mf;
  806. }
  807. private static MouseFlags SetControlKeyStates (MouseFlags buttonState, MouseFlags mouseFlag)
  808. {
  809. if ((buttonState & MouseFlags.ButtonCtrl) != 0 && (mouseFlag & MouseFlags.ButtonCtrl) == 0)
  810. mouseFlag |= MouseFlags.ButtonCtrl;
  811. if ((buttonState & MouseFlags.ButtonShift) != 0 && (mouseFlag & MouseFlags.ButtonShift) == 0)
  812. mouseFlag |= MouseFlags.ButtonShift;
  813. if ((buttonState & MouseFlags.ButtonAlt) != 0 && (mouseFlag & MouseFlags.ButtonAlt) == 0)
  814. mouseFlag |= MouseFlags.ButtonAlt;
  815. return mouseFlag;
  816. }
  817. /// <summary>
  818. /// Get the terminal that holds the console driver.
  819. /// </summary>
  820. /// <param name="process">The process.</param>
  821. /// <returns>If supported the executable console process, null otherwise.</returns>
  822. public static Process GetParentProcess (Process process)
  823. {
  824. if (!RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) {
  825. return null;
  826. }
  827. string query = "SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = " + process.Id;
  828. using (ManagementObjectSearcher mos = new ManagementObjectSearcher (query)) {
  829. foreach (ManagementObject mo in mos.Get ()) {
  830. if (mo ["ParentProcessId"] != null) {
  831. try {
  832. var id = Convert.ToInt32 (mo ["ParentProcessId"]);
  833. return Process.GetProcessById (id);
  834. } catch {
  835. }
  836. }
  837. }
  838. }
  839. return null;
  840. }
  841. }
  842. }