NetDriver.cs 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642
  1. //
  2. // NetDriver.cs: The System.Console-based .NET driver, works on Windows and Unix, but is not particularly efficient.
  3. //
  4. // Authors:
  5. // Miguel de Icaza ([email protected])
  6. //
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Diagnostics;
  10. using System.Linq;
  11. using System.Runtime.InteropServices;
  12. using System.Threading;
  13. using System.Threading.Tasks;
  14. using System.Text;
  15. namespace Terminal.Gui {
  16. internal class NetWinVTConsole {
  17. IntPtr InputHandle, OutputHandle, ErrorHandle;
  18. uint originalInputConsoleMode, originalOutputConsoleMode, originalErrorConsoleMode;
  19. public NetWinVTConsole ()
  20. {
  21. InputHandle = GetStdHandle (STD_INPUT_HANDLE);
  22. if (!GetConsoleMode (InputHandle, out uint mode)) {
  23. throw new ApplicationException ($"Failed to get input console mode, error code: {GetLastError ()}.");
  24. }
  25. originalInputConsoleMode = mode;
  26. if ((mode & ENABLE_VIRTUAL_TERMINAL_INPUT) < ENABLE_VIRTUAL_TERMINAL_INPUT) {
  27. mode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
  28. if (!SetConsoleMode (InputHandle, mode)) {
  29. throw new ApplicationException ($"Failed to set input console mode, error code: {GetLastError ()}.");
  30. }
  31. }
  32. OutputHandle = GetStdHandle (STD_OUTPUT_HANDLE);
  33. if (!GetConsoleMode (OutputHandle, out mode)) {
  34. throw new ApplicationException ($"Failed to get output console mode, error code: {GetLastError ()}.");
  35. }
  36. originalOutputConsoleMode = mode;
  37. if ((mode & (ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) {
  38. mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
  39. if (!SetConsoleMode (OutputHandle, mode)) {
  40. throw new ApplicationException ($"Failed to set output console mode, error code: {GetLastError ()}.");
  41. }
  42. }
  43. ErrorHandle = GetStdHandle (STD_ERROR_HANDLE);
  44. if (!GetConsoleMode (ErrorHandle, out mode)) {
  45. throw new ApplicationException ($"Failed to get error console mode, error code: {GetLastError ()}.");
  46. }
  47. originalErrorConsoleMode = mode;
  48. if ((mode & (DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) {
  49. mode |= DISABLE_NEWLINE_AUTO_RETURN;
  50. if (!SetConsoleMode (ErrorHandle, mode)) {
  51. throw new ApplicationException ($"Failed to set error console mode, error code: {GetLastError ()}.");
  52. }
  53. }
  54. }
  55. public void Cleanup ()
  56. {
  57. if (!SetConsoleMode (InputHandle, originalInputConsoleMode)) {
  58. throw new ApplicationException ($"Failed to restore input console mode, error code: {GetLastError ()}.");
  59. }
  60. if (!SetConsoleMode (OutputHandle, originalOutputConsoleMode)) {
  61. throw new ApplicationException ($"Failed to restore output console mode, error code: {GetLastError ()}.");
  62. }
  63. if (!SetConsoleMode (ErrorHandle, originalErrorConsoleMode)) {
  64. throw new ApplicationException ($"Failed to restore error console mode, error code: {GetLastError ()}.");
  65. }
  66. }
  67. const int STD_INPUT_HANDLE = -10;
  68. const int STD_OUTPUT_HANDLE = -11;
  69. const int STD_ERROR_HANDLE = -12;
  70. // Input modes.
  71. const uint ENABLE_PROCESSED_INPUT = 1;
  72. const uint ENABLE_LINE_INPUT = 2;
  73. const uint ENABLE_ECHO_INPUT = 4;
  74. const uint ENABLE_WINDOW_INPUT = 8;
  75. const uint ENABLE_MOUSE_INPUT = 16;
  76. const uint ENABLE_INSERT_MODE = 32;
  77. const uint ENABLE_QUICK_EDIT_MODE = 64;
  78. const uint ENABLE_EXTENDED_FLAGS = 128;
  79. const uint ENABLE_VIRTUAL_TERMINAL_INPUT = 512;
  80. // Output modes.
  81. const uint ENABLE_PROCESSED_OUTPUT = 1;
  82. const uint ENABLE_WRAP_AT_EOL_OUTPUT = 2;
  83. const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4;
  84. const uint DISABLE_NEWLINE_AUTO_RETURN = 8;
  85. const uint ENABLE_LVB_GRID_WORLDWIDE = 10;
  86. [DllImport ("kernel32.dll", SetLastError = true)]
  87. static extern IntPtr GetStdHandle (int nStdHandle);
  88. [DllImport ("kernel32.dll")]
  89. static extern bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode);
  90. [DllImport ("kernel32.dll")]
  91. static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode);
  92. [DllImport ("kernel32.dll")]
  93. static extern uint GetLastError ();
  94. }
  95. internal class NetEvents {
  96. ManualResetEventSlim inputReady = new ManualResetEventSlim (false);
  97. ManualResetEventSlim waitForStart = new ManualResetEventSlim (false);
  98. ManualResetEventSlim winChange = new ManualResetEventSlim (false);
  99. Queue<InputResult?> inputResultQueue = new Queue<InputResult?> ();
  100. ConsoleDriver consoleDriver;
  101. volatile ConsoleKeyInfo [] cki = null;
  102. static volatile bool isEscSeq;
  103. int lastWindowHeight;
  104. bool stopTasks;
  105. #if PROCESS_REQUEST
  106. bool neededProcessRequest;
  107. #endif
  108. public bool IsTerminalWithOptions { get; set; }
  109. public EscSeqReqProc EscSeqReqProc { get; } = new EscSeqReqProc ();
  110. public NetEvents (ConsoleDriver consoleDriver)
  111. {
  112. if (consoleDriver == null) {
  113. throw new ArgumentNullException ("Console driver instance must be provided.");
  114. }
  115. this.consoleDriver = consoleDriver;
  116. Task.Run (ProcessInputResultQueue);
  117. Task.Run (CheckWinChange);
  118. }
  119. internal void StopTasks ()
  120. {
  121. stopTasks = true;
  122. }
  123. public InputResult? ReadConsoleInput ()
  124. {
  125. while (true) {
  126. if (stopTasks) {
  127. return null;
  128. }
  129. waitForStart.Set ();
  130. winChange.Set ();
  131. if (inputResultQueue.Count == 0) {
  132. inputReady.Wait ();
  133. inputReady.Reset ();
  134. }
  135. #if PROCESS_REQUEST
  136. neededProcessRequest = false;
  137. #endif
  138. if (inputResultQueue.Count > 0) {
  139. return inputResultQueue.Dequeue ();
  140. }
  141. }
  142. }
  143. void ProcessInputResultQueue ()
  144. {
  145. while (true) {
  146. waitForStart.Wait ();
  147. waitForStart.Reset ();
  148. if (inputResultQueue.Count == 0) {
  149. GetConsoleKey ();
  150. }
  151. inputReady.Set ();
  152. }
  153. }
  154. void GetConsoleKey ()
  155. {
  156. ConsoleKey key = 0;
  157. ConsoleModifiers mod = 0;
  158. ConsoleKeyInfo newConsoleKeyInfo = default;
  159. while (true) {
  160. ConsoleKeyInfo consoleKeyInfo = Console.ReadKey (true);
  161. if ((consoleKeyInfo.KeyChar == (char)Key.Esc && !isEscSeq)
  162. || (consoleKeyInfo.KeyChar != (char)Key.Esc && isEscSeq)) {
  163. if (cki == null && consoleKeyInfo.KeyChar != (char)Key.Esc && isEscSeq) {
  164. cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)Key.Esc, 0,
  165. false, false, false), cki);
  166. }
  167. isEscSeq = true;
  168. newConsoleKeyInfo = consoleKeyInfo;
  169. cki = EscSeqUtils.ResizeArray (consoleKeyInfo, cki);
  170. if (!Console.KeyAvailable) {
  171. DecodeEscSeq (ref newConsoleKeyInfo, ref key, cki, ref mod);
  172. cki = null;
  173. isEscSeq = false;
  174. break;
  175. }
  176. } else if (consoleKeyInfo.KeyChar == (char)Key.Esc && isEscSeq && cki != null) {
  177. DecodeEscSeq (ref newConsoleKeyInfo, ref key, cki, ref mod);
  178. cki = null;
  179. break;
  180. } else {
  181. GetConsoleInputType (consoleKeyInfo);
  182. isEscSeq = false;
  183. break;
  184. }
  185. }
  186. }
  187. void CheckWinChange ()
  188. {
  189. while (true) {
  190. if (stopTasks) {
  191. return;
  192. }
  193. winChange.Wait ();
  194. winChange.Reset ();
  195. WaitWinChange ();
  196. inputReady.Set ();
  197. }
  198. }
  199. void WaitWinChange ()
  200. {
  201. while (true) {
  202. // HACK: Sleep for 10ms to mitigate high CPU usage (see issue #1502). 10ms was tested to address the problem, but may not be correct.
  203. Thread.Sleep (10);
  204. if (stopTasks) {
  205. return;
  206. }
  207. switch (IsTerminalWithOptions) {
  208. case false:
  209. int buffHeight, buffWidth;
  210. if (((NetDriver)consoleDriver).IsWinPlatform) {
  211. buffHeight = Math.Max (Console.BufferHeight, 0);
  212. buffWidth = Math.Max (Console.BufferWidth, 0);
  213. } else {
  214. buffHeight = consoleDriver.Rows;
  215. buffWidth = consoleDriver.Cols;
  216. }
  217. if (IsWinChanged (
  218. Math.Max (Console.WindowHeight, 0),
  219. Math.Max (Console.WindowWidth, 0),
  220. buffHeight,
  221. buffWidth)) {
  222. return;
  223. }
  224. break;
  225. case true:
  226. //Request the size of the text area in characters.
  227. EscSeqReqProc.Add ("t");
  228. Console.Out.Write ("\x1b[18t");
  229. break;
  230. }
  231. }
  232. }
  233. bool IsWinChanged (int winHeight, int winWidth, int buffHeight, int buffWidth)
  234. {
  235. if (!consoleDriver.EnableConsoleScrolling) {
  236. if (winWidth != consoleDriver.Cols || winHeight != consoleDriver.Rows) {
  237. var w = Math.Max (winWidth, 0);
  238. var h = Math.Max (winHeight, 0);
  239. GetWindowSizeEvent (new Size (w, h));
  240. return true;
  241. }
  242. } else {
  243. if (winWidth != consoleDriver.Cols || winHeight != lastWindowHeight
  244. || buffWidth != consoleDriver.Cols || buffHeight != consoleDriver.Rows) {
  245. lastWindowHeight = Math.Max (winHeight, 0);
  246. GetWindowSizeEvent (new Size (winWidth, lastWindowHeight));
  247. return true;
  248. }
  249. }
  250. return false;
  251. }
  252. void GetWindowSizeEvent (Size size)
  253. {
  254. WindowSizeEvent windowSizeEvent = new WindowSizeEvent () {
  255. Size = size
  256. };
  257. inputResultQueue.Enqueue (new InputResult () {
  258. EventType = EventType.WindowSize,
  259. WindowSizeEvent = windowSizeEvent
  260. });
  261. }
  262. void GetConsoleInputType (ConsoleKeyInfo consoleKeyInfo)
  263. {
  264. InputResult inputResult = new InputResult {
  265. EventType = EventType.Key
  266. };
  267. MouseEvent mouseEvent = new MouseEvent ();
  268. ConsoleKeyInfo newConsoleKeyInfo = EscSeqUtils.GetConsoleInputKey (consoleKeyInfo);
  269. if (inputResult.EventType == EventType.Key) {
  270. inputResult.ConsoleKeyInfo = newConsoleKeyInfo;
  271. } else {
  272. inputResult.MouseEvent = mouseEvent;
  273. }
  274. inputResultQueue.Enqueue (inputResult);
  275. }
  276. void DecodeEscSeq (ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod)
  277. {
  278. string c1Control, code, terminating;
  279. string [] values;
  280. // isKeyMouse is true if it's CSI<, false otherwise
  281. bool isKeyMouse;
  282. bool isReq;
  283. List<MouseFlags> mouseFlags;
  284. Point pos;
  285. 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);
  286. if (isKeyMouse) {
  287. foreach (var mf in mouseFlags) {
  288. GetMouseEvent (MapMouseFlags (mf), pos);
  289. }
  290. return;
  291. } else if (isReq) {
  292. GetRequestEvent (c1Control, code, values, terminating);
  293. return;
  294. }
  295. InputResult inputResult = new InputResult {
  296. EventType = EventType.Key,
  297. ConsoleKeyInfo = newConsoleKeyInfo
  298. };
  299. inputResultQueue.Enqueue (inputResult);
  300. }
  301. void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos)
  302. {
  303. GetMouseEvent (MapMouseFlags (mouseFlag), pos);
  304. }
  305. MouseButtonState MapMouseFlags (MouseFlags mouseFlags)
  306. {
  307. MouseButtonState mbs = default;
  308. foreach (var flag in Enum.GetValues (mouseFlags.GetType ())) {
  309. if (mouseFlags.HasFlag ((MouseFlags)flag)) {
  310. switch (flag) {
  311. case MouseFlags.Button1Pressed:
  312. mbs |= MouseButtonState.Button1Pressed;
  313. break;
  314. case MouseFlags.Button1Released:
  315. mbs |= MouseButtonState.Button1Released;
  316. break;
  317. case MouseFlags.Button1Clicked:
  318. mbs |= MouseButtonState.Button1Clicked;
  319. break;
  320. case MouseFlags.Button1DoubleClicked:
  321. mbs |= MouseButtonState.Button1DoubleClicked;
  322. break;
  323. case MouseFlags.Button1TripleClicked:
  324. mbs |= MouseButtonState.Button1TripleClicked;
  325. break;
  326. case MouseFlags.Button2Pressed:
  327. mbs |= MouseButtonState.Button2Pressed;
  328. break;
  329. case MouseFlags.Button2Released:
  330. mbs |= MouseButtonState.Button2Released;
  331. break;
  332. case MouseFlags.Button2Clicked:
  333. mbs |= MouseButtonState.Button2Clicked;
  334. break;
  335. case MouseFlags.Button2DoubleClicked:
  336. mbs |= MouseButtonState.Button2DoubleClicked;
  337. break;
  338. case MouseFlags.Button2TripleClicked:
  339. mbs |= MouseButtonState.Button2TripleClicked;
  340. break;
  341. case MouseFlags.Button3Pressed:
  342. mbs |= MouseButtonState.Button3Pressed;
  343. break;
  344. case MouseFlags.Button3Released:
  345. mbs |= MouseButtonState.Button3Released;
  346. break;
  347. case MouseFlags.Button3Clicked:
  348. mbs |= MouseButtonState.Button3Clicked;
  349. break;
  350. case MouseFlags.Button3DoubleClicked:
  351. mbs |= MouseButtonState.Button3DoubleClicked;
  352. break;
  353. case MouseFlags.Button3TripleClicked:
  354. mbs |= MouseButtonState.Button3TripleClicked;
  355. break;
  356. case MouseFlags.WheeledUp:
  357. mbs |= MouseButtonState.ButtonWheeledUp;
  358. break;
  359. case MouseFlags.WheeledDown:
  360. mbs |= MouseButtonState.ButtonWheeledDown;
  361. break;
  362. case MouseFlags.WheeledLeft:
  363. mbs |= MouseButtonState.ButtonWheeledLeft;
  364. break;
  365. case MouseFlags.WheeledRight:
  366. mbs |= MouseButtonState.ButtonWheeledRight;
  367. break;
  368. case MouseFlags.Button4Pressed:
  369. mbs |= MouseButtonState.Button4Pressed;
  370. break;
  371. case MouseFlags.Button4Released:
  372. mbs |= MouseButtonState.Button4Released;
  373. break;
  374. case MouseFlags.Button4Clicked:
  375. mbs |= MouseButtonState.Button4Clicked;
  376. break;
  377. case MouseFlags.Button4DoubleClicked:
  378. mbs |= MouseButtonState.Button4DoubleClicked;
  379. break;
  380. case MouseFlags.Button4TripleClicked:
  381. mbs |= MouseButtonState.Button4TripleClicked;
  382. break;
  383. case MouseFlags.ButtonShift:
  384. mbs |= MouseButtonState.ButtonShift;
  385. break;
  386. case MouseFlags.ButtonCtrl:
  387. mbs |= MouseButtonState.ButtonCtrl;
  388. break;
  389. case MouseFlags.ButtonAlt:
  390. mbs |= MouseButtonState.ButtonAlt;
  391. break;
  392. case MouseFlags.ReportMousePosition:
  393. mbs |= MouseButtonState.ReportMousePosition;
  394. break;
  395. case MouseFlags.AllEvents:
  396. mbs |= MouseButtonState.AllEvents;
  397. break;
  398. }
  399. }
  400. }
  401. return mbs;
  402. }
  403. Point lastCursorPosition;
  404. void GetRequestEvent (string c1Control, string code, string [] values, string terminating)
  405. {
  406. EventType eventType = new EventType ();
  407. switch (terminating) {
  408. case "R": // Reports cursor position as CSI r ; c R
  409. Point point = new Point {
  410. X = int.Parse (values [1]) - 1,
  411. Y = int.Parse (values [0]) - 1
  412. };
  413. if (lastCursorPosition.Y != point.Y) {
  414. lastCursorPosition = point;
  415. eventType = EventType.WindowPosition;
  416. var winPositionEv = new WindowPositionEvent () {
  417. CursorPosition = point
  418. };
  419. inputResultQueue.Enqueue (new InputResult () {
  420. EventType = eventType,
  421. WindowPositionEvent = winPositionEv
  422. });
  423. } else {
  424. return;
  425. }
  426. break;
  427. case "c":
  428. try {
  429. var parent = EscSeqUtils.GetParentProcess (Process.GetCurrentProcess ());
  430. if (parent == null) { Debug.WriteLine ("Not supported!"); }
  431. } catch (Exception ex) {
  432. Debug.WriteLine (ex.Message);
  433. }
  434. if (c1Control == "CSI" && values.Length == 2
  435. && values [0] == "1" && values [1] == "0") {
  436. // Reports CSI?1;0c ("VT101 with No Options")
  437. IsTerminalWithOptions = false;
  438. } else {
  439. IsTerminalWithOptions = true;
  440. }
  441. break;
  442. case "t":
  443. switch (values [0]) {
  444. case "8":
  445. IsWinChanged (
  446. Math.Max (int.Parse (values [1]), 0),
  447. Math.Max (int.Parse (values [2]), 0),
  448. Math.Max (int.Parse (values [1]), 0),
  449. Math.Max (int.Parse (values [2]), 0));
  450. break;
  451. default:
  452. SetRequestedEvent (c1Control, code, values, terminating);
  453. break;
  454. }
  455. break;
  456. default:
  457. SetRequestedEvent (c1Control, code, values, terminating);
  458. break;
  459. }
  460. inputReady.Set ();
  461. }
  462. void SetRequestedEvent (string c1Control, string code, string [] values, string terminating)
  463. {
  464. EventType eventType = EventType.RequestResponse;
  465. var requestRespEv = new RequestResponseEvent () {
  466. ResultTuple = (c1Control, code, values, terminating)
  467. };
  468. inputResultQueue.Enqueue (new InputResult () {
  469. EventType = eventType,
  470. RequestResponseEvent = requestRespEv
  471. });
  472. }
  473. void GetMouseEvent (MouseButtonState buttonState, Point pos)
  474. {
  475. MouseEvent mouseEvent = new MouseEvent () {
  476. Position = pos,
  477. ButtonState = buttonState,
  478. };
  479. inputResultQueue.Enqueue (new InputResult () {
  480. EventType = EventType.Mouse,
  481. MouseEvent = mouseEvent
  482. });
  483. inputReady.Set ();
  484. }
  485. public enum EventType {
  486. Key = 1,
  487. Mouse = 2,
  488. WindowSize = 3,
  489. WindowPosition = 4,
  490. RequestResponse = 5
  491. }
  492. [Flags]
  493. public enum MouseButtonState {
  494. Button1Pressed = 0x1,
  495. Button1Released = 0x2,
  496. Button1Clicked = 0x4,
  497. Button1DoubleClicked = 0x8,
  498. Button1TripleClicked = 0x10,
  499. Button2Pressed = 0x20,
  500. Button2Released = 0x40,
  501. Button2Clicked = 0x80,
  502. Button2DoubleClicked = 0x100,
  503. Button2TripleClicked = 0x200,
  504. Button3Pressed = 0x400,
  505. Button3Released = 0x800,
  506. Button3Clicked = 0x1000,
  507. Button3DoubleClicked = 0x2000,
  508. Button3TripleClicked = 0x4000,
  509. ButtonWheeledUp = 0x8000,
  510. ButtonWheeledDown = 0x10000,
  511. ButtonWheeledLeft = 0x20000,
  512. ButtonWheeledRight = 0x40000,
  513. Button4Pressed = 0x80000,
  514. Button4Released = 0x100000,
  515. Button4Clicked = 0x200000,
  516. Button4DoubleClicked = 0x400000,
  517. Button4TripleClicked = 0x800000,
  518. ButtonShift = 0x1000000,
  519. ButtonCtrl = 0x2000000,
  520. ButtonAlt = 0x4000000,
  521. ReportMousePosition = 0x8000000,
  522. AllEvents = -1
  523. }
  524. public struct MouseEvent {
  525. public Point Position;
  526. public MouseButtonState ButtonState;
  527. }
  528. public struct WindowSizeEvent {
  529. public Size Size;
  530. }
  531. public struct WindowPositionEvent {
  532. public int Top;
  533. public int Left;
  534. public Point CursorPosition;
  535. }
  536. public struct RequestResponseEvent {
  537. public (string c1Control, string code, string [] values, string terminating) ResultTuple;
  538. }
  539. public struct InputResult {
  540. public EventType EventType;
  541. public ConsoleKeyInfo ConsoleKeyInfo;
  542. public MouseEvent MouseEvent;
  543. public WindowSizeEvent WindowSizeEvent;
  544. public WindowPositionEvent WindowPositionEvent;
  545. public RequestResponseEvent RequestResponseEvent;
  546. }
  547. }
  548. internal class NetDriver : ConsoleDriver {
  549. const int COLOR_BLACK = 30;
  550. const int COLOR_RED = 31;
  551. const int COLOR_GREEN = 32;
  552. const int COLOR_YELLOW = 33;
  553. const int COLOR_BLUE = 34;
  554. const int COLOR_MAGENTA = 35;
  555. const int COLOR_CYAN = 36;
  556. const int COLOR_WHITE = 37;
  557. const int COLOR_BRIGHT_BLACK = 90;
  558. const int COLOR_BRIGHT_RED = 91;
  559. const int COLOR_BRIGHT_GREEN = 92;
  560. const int COLOR_BRIGHT_YELLOW = 93;
  561. const int COLOR_BRIGHT_BLUE = 94;
  562. const int COLOR_BRIGHT_MAGENTA = 95;
  563. const int COLOR_BRIGHT_CYAN = 96;
  564. const int COLOR_BRIGHT_WHITE = 97;
  565. int cols, rows, left, top;
  566. public override int Cols => cols;
  567. public override int Rows => rows;
  568. public override int Left => left;
  569. public override int Top => top;
  570. public override bool EnableConsoleScrolling { get; set; }
  571. public NetWinVTConsole NetWinConsole { get; }
  572. public bool IsWinPlatform { get; }
  573. public override IClipboard Clipboard { get; }
  574. public override int [,,] Contents => contents;
  575. int largestBufferHeight;
  576. public NetDriver ()
  577. {
  578. var p = Environment.OSVersion.Platform;
  579. if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
  580. IsWinPlatform = true;
  581. NetWinConsole = new NetWinVTConsole ();
  582. }
  583. if (IsWinPlatform) {
  584. Clipboard = new WindowsClipboard ();
  585. } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
  586. Clipboard = new MacOSXClipboard ();
  587. } else {
  588. if (CursesDriver.Is_WSL_Platform ()) {
  589. Clipboard = new WSLClipboard ();
  590. } else {
  591. Clipboard = new CursesClipboard ();
  592. }
  593. }
  594. }
  595. // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
  596. int [,,] contents;
  597. bool [] dirtyLine;
  598. static bool sync = false;
  599. // Current row, and current col, tracked by Move/AddCh only
  600. int ccol, crow;
  601. public override void Move (int col, int row)
  602. {
  603. ccol = col;
  604. crow = row;
  605. }
  606. public override void AddRune (Rune rune)
  607. {
  608. if (contents.Length != Rows * Cols * 3) {
  609. return;
  610. }
  611. rune = rune.MakePrintable ();
  612. var runeWidth = rune.GetColumns ();
  613. var validClip = IsValidContent (ccol, crow, Clip);
  614. if (validClip) {
  615. if (runeWidth == 0 && ccol > 0) {
  616. var r = contents [crow, ccol - 1, 0];
  617. var s = new string (new char [] { (char)r, (char)rune.Value });
  618. string sn;
  619. if (!s.IsNormalized ()) {
  620. sn = s.Normalize ();
  621. } else {
  622. sn = s;
  623. }
  624. var c = sn [0];
  625. contents [crow, ccol - 1, 0] = c;
  626. contents [crow, ccol - 1, 1] = CurrentAttribute;
  627. contents [crow, ccol - 1, 2] = 1;
  628. } else {
  629. if (runeWidth < 2 && ccol > 0
  630. && ((Rune)(char)contents [crow, ccol - 1, 0]).GetColumns () > 1) {
  631. contents [crow, ccol - 1, 0] = (int)(uint)' ';
  632. } else if (runeWidth < 2 && ccol <= Clip.Right - 1
  633. && ((Rune)(char)contents [crow, ccol, 0]).GetColumns () > 1) {
  634. contents [crow, ccol + 1, 0] = (int)(uint)' ';
  635. contents [crow, ccol + 1, 2] = 1;
  636. }
  637. if (runeWidth > 1 && ccol == Clip.Right - 1) {
  638. contents [crow, ccol, 0] = (int)(uint)' ';
  639. } else {
  640. contents [crow, ccol, 0] = (int)(uint)rune.Value;
  641. }
  642. contents [crow, ccol, 1] = CurrentAttribute;
  643. contents [crow, ccol, 2] = 1;
  644. }
  645. dirtyLine [crow] = true;
  646. }
  647. if (runeWidth < 0 || runeWidth > 0) {
  648. ccol++;
  649. }
  650. if (runeWidth > 1) {
  651. if (validClip && ccol < Clip.Right) {
  652. contents [crow, ccol, 1] = CurrentAttribute;
  653. contents [crow, ccol, 2] = 0;
  654. }
  655. ccol++;
  656. }
  657. if (sync) {
  658. UpdateScreen ();
  659. }
  660. }
  661. public override void AddStr (string str)
  662. {
  663. foreach (var rune in str.EnumerateRunes ())
  664. AddRune (rune);
  665. }
  666. public override void End ()
  667. {
  668. mainLoop.netEvents.StopTasks ();
  669. if (IsWinPlatform) {
  670. NetWinConsole.Cleanup ();
  671. }
  672. StopReportingMouseMoves ();
  673. Console.ResetColor ();
  674. //Disable alternative screen buffer.
  675. Console.Out.Write ("\x1b[?1049l");
  676. //Set cursor key to cursor.
  677. Console.Out.Write ("\x1b[?25h");
  678. Console.Out.Close ();
  679. }
  680. public override Attribute MakeColor (Color foreground, Color background)
  681. {
  682. return MakeColor ((ConsoleColor)foreground, (ConsoleColor)background);
  683. }
  684. static Attribute MakeColor (ConsoleColor f, ConsoleColor b)
  685. {
  686. // Encode the colors into the int value.
  687. return new Attribute (
  688. value: ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff),
  689. foreground: (Color)f,
  690. background: (Color)b
  691. );
  692. }
  693. public override void Init (Action terminalResized)
  694. {
  695. TerminalResized = terminalResized;
  696. //Enable alternative screen buffer.
  697. Console.Out.Write ("\x1b[?1049h");
  698. //Set cursor key to application.
  699. Console.Out.Write ("\x1b[?25l");
  700. Console.TreatControlCAsInput = true;
  701. if (EnableConsoleScrolling) {
  702. largestBufferHeight = Console.BufferHeight;
  703. } else {
  704. largestBufferHeight = Console.WindowHeight;
  705. }
  706. cols = Console.WindowWidth;
  707. rows = largestBufferHeight;
  708. CurrentAttribute = MakeColor (Color.White, Color.Black);
  709. InitalizeColorSchemes ();
  710. CurrentAttribute = MakeColor (Color.White, Color.Black);
  711. InitalizeColorSchemes ();
  712. ResizeScreen ();
  713. UpdateOffScreen ();
  714. StartReportingMouseMoves ();
  715. }
  716. public override void ResizeScreen ()
  717. {
  718. if (!EnableConsoleScrolling) {
  719. if (Console.WindowHeight > 0) {
  720. // Not supported on Unix.
  721. if (IsWinPlatform) {
  722. // Can raise an exception while is still resizing.
  723. try {
  724. #pragma warning disable CA1416
  725. Console.CursorTop = 0;
  726. Console.CursorLeft = 0;
  727. Console.WindowTop = 0;
  728. Console.WindowLeft = 0;
  729. if (Console.WindowHeight > Rows) {
  730. Console.SetWindowSize (Cols, Rows);
  731. }
  732. Console.SetBufferSize (Cols, Rows);
  733. #pragma warning restore CA1416
  734. } catch (System.IO.IOException) {
  735. setClip ();
  736. } catch (ArgumentOutOfRangeException) {
  737. setClip ();
  738. }
  739. } else {
  740. Console.Out.Write ($"\x1b[8;{Rows};{Cols}t");
  741. }
  742. }
  743. } else {
  744. if (IsWinPlatform) {
  745. if (Console.WindowHeight > 0) {
  746. // Can raise an exception while is still resizing.
  747. try {
  748. #pragma warning disable CA1416
  749. Console.CursorTop = 0;
  750. Console.CursorLeft = 0;
  751. if (Console.WindowHeight > Rows) {
  752. Console.SetWindowSize (Cols, Rows);
  753. }
  754. Console.SetBufferSize (Cols, Rows);
  755. #pragma warning restore CA1416
  756. } catch (System.IO.IOException) {
  757. setClip ();
  758. } catch (ArgumentOutOfRangeException) {
  759. setClip ();
  760. }
  761. }
  762. } else {
  763. Console.Out.Write ($"\x1b[30;{Rows};{Cols}t");
  764. }
  765. }
  766. setClip ();
  767. void setClip ()
  768. {
  769. Clip = new Rect (0, 0, Cols, Rows);
  770. }
  771. }
  772. public override void UpdateOffScreen ()
  773. {
  774. contents = new int [Rows, Cols, 3];
  775. dirtyLine = new bool [Rows];
  776. lock (contents) {
  777. // Can raise an exception while is still resizing.
  778. try {
  779. for (int row = 0; row < rows; row++) {
  780. for (int c = 0; c < cols; c++) {
  781. contents [row, c, 0] = ' ';
  782. contents [row, c, 1] = (ushort)Colors.TopLevel.Normal;
  783. contents [row, c, 2] = 0;
  784. dirtyLine [row] = true;
  785. }
  786. }
  787. } catch (IndexOutOfRangeException) { }
  788. }
  789. }
  790. public override Attribute MakeAttribute (Color fore, Color back)
  791. {
  792. return MakeColor ((ConsoleColor)fore, (ConsoleColor)back);
  793. }
  794. public override void Refresh ()
  795. {
  796. UpdateScreen ();
  797. UpdateCursor ();
  798. }
  799. public override void UpdateScreen ()
  800. {
  801. if (winChanging || Console.WindowHeight < 1 || contents.Length != Rows * Cols * 3
  802. || (!EnableConsoleScrolling && Rows != Console.WindowHeight)
  803. || (EnableConsoleScrolling && Rows != largestBufferHeight)) {
  804. return;
  805. }
  806. int top = 0;
  807. int left = 0;
  808. int rows = Rows;
  809. int cols = Cols;
  810. System.Text.StringBuilder output = new System.Text.StringBuilder ();
  811. int redrawAttr = -1;
  812. var lastCol = -1;
  813. Console.CursorVisible = false;
  814. for (int row = top; row < rows; row++) {
  815. if (Console.WindowHeight < 1) {
  816. return;
  817. }
  818. if (!dirtyLine [row]) {
  819. continue;
  820. }
  821. if (!SetCursorPosition (0, row)) {
  822. return;
  823. }
  824. dirtyLine [row] = false;
  825. output.Clear ();
  826. for (int col = left; col < cols; col++) {
  827. lastCol = -1;
  828. var outputWidth = 0;
  829. for (; col < cols; col++) {
  830. if (contents [row, col, 2] == 0) {
  831. if (output.Length > 0) {
  832. SetCursorPosition (lastCol, row);
  833. Console.Write (output);
  834. output.Clear ();
  835. lastCol += outputWidth;
  836. outputWidth = 0;
  837. } else if (lastCol == -1) {
  838. lastCol = col;
  839. }
  840. if (lastCol + 1 < cols)
  841. lastCol++;
  842. continue;
  843. }
  844. if (lastCol == -1)
  845. lastCol = col;
  846. var attr = contents [row, col, 1];
  847. if (attr != redrawAttr) {
  848. redrawAttr = attr;
  849. output.Append (WriteAttributes (attr));
  850. }
  851. outputWidth++;
  852. var rune = (Rune)contents [row, col, 0];
  853. char [] spair;
  854. if (rune.DecodeSurrogatePair (out spair)) {
  855. output.Append (spair);
  856. } else {
  857. output.Append ((char)rune.Value);
  858. }
  859. contents [row, col, 2] = 0;
  860. }
  861. }
  862. if (output.Length > 0) {
  863. SetCursorPosition (lastCol, row);
  864. Console.Write (output);
  865. }
  866. }
  867. SetCursorPosition (0, 0);
  868. }
  869. void SetVirtualCursorPosition (int col, int row)
  870. {
  871. Console.Out.Write ($"\x1b[{row + 1};{col + 1}H");
  872. }
  873. System.Text.StringBuilder WriteAttributes (int attr)
  874. {
  875. const string CSI = "\x1b[";
  876. int bg = 0;
  877. int fg = 0;
  878. System.Text.StringBuilder sb = new System.Text.StringBuilder ();
  879. IEnumerable<int> values = Enum.GetValues (typeof (ConsoleColor))
  880. .OfType<ConsoleColor> ()
  881. .Select (s => (int)s);
  882. if (values.Contains (attr & 0xffff)) {
  883. bg = MapColors ((ConsoleColor)(attr & 0xffff), false);
  884. }
  885. if (values.Contains ((attr >> 16) & 0xffff)) {
  886. fg = MapColors ((ConsoleColor)((attr >> 16) & 0xffff));
  887. }
  888. sb.Append ($"{CSI}{bg};{fg}m");
  889. return sb;
  890. }
  891. int MapColors (ConsoleColor color, bool isForeground = true)
  892. {
  893. switch (color) {
  894. case ConsoleColor.Black:
  895. return isForeground ? COLOR_BLACK : COLOR_BLACK + 10;
  896. case ConsoleColor.DarkBlue:
  897. return isForeground ? COLOR_BLUE : COLOR_BLUE + 10;
  898. case ConsoleColor.DarkGreen:
  899. return isForeground ? COLOR_GREEN : COLOR_GREEN + 10;
  900. case ConsoleColor.DarkCyan:
  901. return isForeground ? COLOR_CYAN : COLOR_CYAN + 10;
  902. case ConsoleColor.DarkRed:
  903. return isForeground ? COLOR_RED : COLOR_RED + 10;
  904. case ConsoleColor.DarkMagenta:
  905. return isForeground ? COLOR_MAGENTA : COLOR_MAGENTA + 10;
  906. case ConsoleColor.DarkYellow:
  907. return isForeground ? COLOR_YELLOW : COLOR_YELLOW + 10;
  908. case ConsoleColor.Gray:
  909. return isForeground ? COLOR_WHITE : COLOR_WHITE + 10;
  910. case ConsoleColor.DarkGray:
  911. return isForeground ? COLOR_BRIGHT_BLACK : COLOR_BRIGHT_BLACK + 10;
  912. case ConsoleColor.Blue:
  913. return isForeground ? COLOR_BRIGHT_BLUE : COLOR_BRIGHT_BLUE + 10;
  914. case ConsoleColor.Green:
  915. return isForeground ? COLOR_BRIGHT_GREEN : COLOR_BRIGHT_GREEN + 10;
  916. case ConsoleColor.Cyan:
  917. return isForeground ? COLOR_BRIGHT_CYAN : COLOR_BRIGHT_CYAN + 10;
  918. case ConsoleColor.Red:
  919. return isForeground ? COLOR_BRIGHT_RED : COLOR_BRIGHT_RED + 10;
  920. case ConsoleColor.Magenta:
  921. return isForeground ? COLOR_BRIGHT_MAGENTA : COLOR_BRIGHT_MAGENTA + 10;
  922. case ConsoleColor.Yellow:
  923. return isForeground ? COLOR_BRIGHT_YELLOW : COLOR_BRIGHT_YELLOW + 10;
  924. case ConsoleColor.White:
  925. return isForeground ? COLOR_BRIGHT_WHITE : COLOR_BRIGHT_WHITE + 10;
  926. }
  927. return 0;
  928. }
  929. bool SetCursorPosition (int col, int row)
  930. {
  931. if (IsWinPlatform) {
  932. // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth.
  933. try {
  934. Console.SetCursorPosition (col, row);
  935. return true;
  936. } catch (Exception) {
  937. return false;
  938. }
  939. } else {
  940. SetVirtualCursorPosition (col, row);
  941. return true;
  942. }
  943. }
  944. private void SetWindowPosition (int col, int row)
  945. {
  946. if (IsWinPlatform && EnableConsoleScrolling) {
  947. var winTop = Math.Max (Rows - Console.WindowHeight - row, 0);
  948. winTop = Math.Min (winTop, Rows - Console.WindowHeight + 1);
  949. winTop = Math.Max (winTop, 0);
  950. if (winTop != Console.WindowTop) {
  951. try {
  952. if (!EnsureBufferSize ()) {
  953. return;
  954. }
  955. #pragma warning disable CA1416
  956. Console.SetWindowPosition (col, winTop);
  957. #pragma warning restore CA1416
  958. } catch (System.IO.IOException) {
  959. } catch (System.ArgumentOutOfRangeException) { }
  960. }
  961. }
  962. top = Console.WindowTop;
  963. left = Console.WindowLeft;
  964. }
  965. private bool EnsureBufferSize ()
  966. {
  967. #pragma warning disable CA1416
  968. if (IsWinPlatform && Console.BufferHeight < Rows) {
  969. try {
  970. Console.SetBufferSize (Console.WindowWidth, Rows);
  971. } catch (Exception) {
  972. return false;
  973. }
  974. }
  975. #pragma warning restore CA1416
  976. return true;
  977. }
  978. private CursorVisibility? savedCursorVisibility;
  979. public override void UpdateCursor ()
  980. {
  981. EnsureCursorVisibility ();
  982. //Debug.WriteLine ($"Before - CursorTop: {Console.CursorTop};CursorLeft: {Console.CursorLeft}");
  983. if (ccol >= 0 && ccol < Cols && crow >= 0 && crow < Rows) {
  984. SetCursorPosition (ccol, crow);
  985. SetWindowPosition (0, crow);
  986. }
  987. //Debug.WriteLine ($"WindowTop: {Console.WindowTop};WindowLeft: {Console.WindowLeft}");
  988. //Debug.WriteLine ($"After - CursorTop: {Console.CursorTop};CursorLeft: {Console.CursorLeft}");
  989. }
  990. public override void StartReportingMouseMoves ()
  991. {
  992. Console.Out.Write (EscSeqUtils.EnableMouseEvents);
  993. }
  994. public override void StopReportingMouseMoves ()
  995. {
  996. Console.Out.Write (EscSeqUtils.DisableMouseEvents);
  997. }
  998. public override void Suspend ()
  999. {
  1000. }
  1001. public override void SetAttribute (Attribute c)
  1002. {
  1003. base.SetAttribute (c);
  1004. }
  1005. public ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
  1006. {
  1007. if (consoleKeyInfo.Key != ConsoleKey.Packet) {
  1008. return consoleKeyInfo;
  1009. }
  1010. var mod = consoleKeyInfo.Modifiers;
  1011. var shift = (mod & ConsoleModifiers.Shift) != 0;
  1012. var alt = (mod & ConsoleModifiers.Alt) != 0;
  1013. var control = (mod & ConsoleModifiers.Control) != 0;
  1014. var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out uint virtualKey, out _);
  1015. return new ConsoleKeyInfo ((char)keyChar, (ConsoleKey)virtualKey, shift, alt, control);
  1016. }
  1017. Key MapKey (ConsoleKeyInfo keyInfo)
  1018. {
  1019. MapKeyModifiers (keyInfo, (Key)keyInfo.Key);
  1020. switch (keyInfo.Key) {
  1021. case ConsoleKey.Escape:
  1022. return MapKeyModifiers (keyInfo, Key.Esc);
  1023. case ConsoleKey.Tab:
  1024. return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab;
  1025. case ConsoleKey.Home:
  1026. return MapKeyModifiers (keyInfo, Key.Home);
  1027. case ConsoleKey.End:
  1028. return MapKeyModifiers (keyInfo, Key.End);
  1029. case ConsoleKey.LeftArrow:
  1030. return MapKeyModifiers (keyInfo, Key.CursorLeft);
  1031. case ConsoleKey.RightArrow:
  1032. return MapKeyModifiers (keyInfo, Key.CursorRight);
  1033. case ConsoleKey.UpArrow:
  1034. return MapKeyModifiers (keyInfo, Key.CursorUp);
  1035. case ConsoleKey.DownArrow:
  1036. return MapKeyModifiers (keyInfo, Key.CursorDown);
  1037. case ConsoleKey.PageUp:
  1038. return MapKeyModifiers (keyInfo, Key.PageUp);
  1039. case ConsoleKey.PageDown:
  1040. return MapKeyModifiers (keyInfo, Key.PageDown);
  1041. case ConsoleKey.Enter:
  1042. return MapKeyModifiers (keyInfo, Key.Enter);
  1043. case ConsoleKey.Spacebar:
  1044. return MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar);
  1045. case ConsoleKey.Backspace:
  1046. return MapKeyModifiers (keyInfo, Key.Backspace);
  1047. case ConsoleKey.Delete:
  1048. return MapKeyModifiers (keyInfo, Key.DeleteChar);
  1049. case ConsoleKey.Insert:
  1050. return MapKeyModifiers (keyInfo, Key.InsertChar);
  1051. case ConsoleKey.Oem1:
  1052. case ConsoleKey.Oem2:
  1053. case ConsoleKey.Oem3:
  1054. case ConsoleKey.Oem4:
  1055. case ConsoleKey.Oem5:
  1056. case ConsoleKey.Oem6:
  1057. case ConsoleKey.Oem7:
  1058. case ConsoleKey.Oem8:
  1059. case ConsoleKey.Oem102:
  1060. case ConsoleKey.OemPeriod:
  1061. case ConsoleKey.OemComma:
  1062. case ConsoleKey.OemPlus:
  1063. case ConsoleKey.OemMinus:
  1064. return (Key)((uint)keyInfo.KeyChar);
  1065. }
  1066. var key = keyInfo.Key;
  1067. if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
  1068. var delta = key - ConsoleKey.A;
  1069. if (keyInfo.Modifiers == ConsoleModifiers.Control) {
  1070. return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta));
  1071. }
  1072. if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
  1073. return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta));
  1074. }
  1075. if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
  1076. if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) {
  1077. return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta));
  1078. }
  1079. }
  1080. return (Key)((uint)keyInfo.KeyChar);
  1081. }
  1082. if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
  1083. var delta = key - ConsoleKey.D0;
  1084. if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
  1085. return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta));
  1086. }
  1087. if (keyInfo.Modifiers == ConsoleModifiers.Control) {
  1088. return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta));
  1089. }
  1090. if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
  1091. if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) {
  1092. return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
  1093. }
  1094. }
  1095. return (Key)((uint)keyInfo.KeyChar);
  1096. }
  1097. if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) {
  1098. var delta = key - ConsoleKey.F1;
  1099. if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
  1100. return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta));
  1101. }
  1102. return (Key)((uint)Key.F1 + delta);
  1103. }
  1104. if (keyInfo.KeyChar != 0) {
  1105. return MapKeyModifiers (keyInfo, (Key)((uint)keyInfo.KeyChar));
  1106. }
  1107. return (Key)(0xffffffff);
  1108. }
  1109. KeyModifiers keyModifiers;
  1110. Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
  1111. {
  1112. if (keyModifiers == null) {
  1113. keyModifiers = new KeyModifiers ();
  1114. }
  1115. Key keyMod = new Key ();
  1116. if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) {
  1117. keyMod = Key.ShiftMask;
  1118. keyModifiers.Shift = true;
  1119. }
  1120. if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) {
  1121. keyMod |= Key.CtrlMask;
  1122. keyModifiers.Ctrl = true;
  1123. }
  1124. if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) {
  1125. keyMod |= Key.AltMask;
  1126. keyModifiers.Alt = true;
  1127. }
  1128. return keyMod != Key.Null ? keyMod | key : key;
  1129. }
  1130. Action<KeyEvent> keyHandler;
  1131. Action<KeyEvent> keyDownHandler;
  1132. Action<KeyEvent> keyUpHandler;
  1133. Action<MouseEvent> mouseHandler;
  1134. NetMainLoop mainLoop;
  1135. public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
  1136. {
  1137. this.keyHandler = keyHandler;
  1138. this.keyDownHandler = keyDownHandler;
  1139. this.keyUpHandler = keyUpHandler;
  1140. this.mouseHandler = mouseHandler;
  1141. var mLoop = this.mainLoop = mainLoop.Driver as NetMainLoop;
  1142. // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will be simulated to be called.
  1143. mLoop.ProcessInput = (e) => ProcessInput (e);
  1144. // Check if terminal supports requests
  1145. this.mainLoop.netEvents.EscSeqReqProc.Add ("c");
  1146. Console.Out.Write ("\x1b[0c");
  1147. }
  1148. void ProcessInput (NetEvents.InputResult inputEvent)
  1149. {
  1150. switch (inputEvent.EventType) {
  1151. case NetEvents.EventType.Key:
  1152. ConsoleKeyInfo consoleKeyInfo = inputEvent.ConsoleKeyInfo;
  1153. if (consoleKeyInfo.Key == ConsoleKey.Packet) {
  1154. consoleKeyInfo = FromVKPacketToKConsoleKeyInfo (consoleKeyInfo);
  1155. }
  1156. keyModifiers = new KeyModifiers ();
  1157. var map = MapKey (consoleKeyInfo);
  1158. if (map == (Key)0xffffffff) {
  1159. return;
  1160. }
  1161. if (map == Key.Null) {
  1162. keyDownHandler (new KeyEvent (map, keyModifiers));
  1163. keyUpHandler (new KeyEvent (map, keyModifiers));
  1164. } else {
  1165. keyDownHandler (new KeyEvent (map, keyModifiers));
  1166. keyHandler (new KeyEvent (map, keyModifiers));
  1167. keyUpHandler (new KeyEvent (map, keyModifiers));
  1168. }
  1169. break;
  1170. case NetEvents.EventType.Mouse:
  1171. mouseHandler (ToDriverMouse (inputEvent.MouseEvent));
  1172. break;
  1173. case NetEvents.EventType.WindowSize:
  1174. ChangeWin (inputEvent.WindowSizeEvent.Size);
  1175. break;
  1176. case NetEvents.EventType.RequestResponse:
  1177. Application.Top.Data = inputEvent.RequestResponseEvent.ResultTuple;
  1178. break;
  1179. }
  1180. }
  1181. volatile bool winChanging;
  1182. void ChangeWin (Size size)
  1183. {
  1184. winChanging = true;
  1185. if (!EnableConsoleScrolling) {
  1186. largestBufferHeight = Math.Max (size.Height, 0);
  1187. } else {
  1188. largestBufferHeight = Math.Max (size.Height, largestBufferHeight);
  1189. }
  1190. top = 0;
  1191. left = 0;
  1192. cols = size.Width;
  1193. rows = largestBufferHeight;
  1194. ResizeScreen ();
  1195. UpdateOffScreen ();
  1196. winChanging = false;
  1197. TerminalResized?.Invoke ();
  1198. }
  1199. MouseEvent ToDriverMouse (NetEvents.MouseEvent me)
  1200. {
  1201. //System.Diagnostics.Debug.WriteLine ($"X: {me.Position.X}; Y: {me.Position.Y}; ButtonState: {me.ButtonState}");
  1202. MouseFlags mouseFlag = 0;
  1203. if ((me.ButtonState & NetEvents.MouseButtonState.Button1Pressed) != 0) {
  1204. mouseFlag |= MouseFlags.Button1Pressed;
  1205. }
  1206. if ((me.ButtonState & NetEvents.MouseButtonState.Button1Released) != 0) {
  1207. mouseFlag |= MouseFlags.Button1Released;
  1208. }
  1209. if ((me.ButtonState & NetEvents.MouseButtonState.Button1Clicked) != 0) {
  1210. mouseFlag |= MouseFlags.Button1Clicked;
  1211. }
  1212. if ((me.ButtonState & NetEvents.MouseButtonState.Button1DoubleClicked) != 0) {
  1213. mouseFlag |= MouseFlags.Button1DoubleClicked;
  1214. }
  1215. if ((me.ButtonState & NetEvents.MouseButtonState.Button1TripleClicked) != 0) {
  1216. mouseFlag |= MouseFlags.Button1TripleClicked;
  1217. }
  1218. if ((me.ButtonState & NetEvents.MouseButtonState.Button2Pressed) != 0) {
  1219. mouseFlag |= MouseFlags.Button2Pressed;
  1220. }
  1221. if ((me.ButtonState & NetEvents.MouseButtonState.Button2Released) != 0) {
  1222. mouseFlag |= MouseFlags.Button2Released;
  1223. }
  1224. if ((me.ButtonState & NetEvents.MouseButtonState.Button2Clicked) != 0) {
  1225. mouseFlag |= MouseFlags.Button2Clicked;
  1226. }
  1227. if ((me.ButtonState & NetEvents.MouseButtonState.Button2DoubleClicked) != 0) {
  1228. mouseFlag |= MouseFlags.Button2DoubleClicked;
  1229. }
  1230. if ((me.ButtonState & NetEvents.MouseButtonState.Button2TripleClicked) != 0) {
  1231. mouseFlag |= MouseFlags.Button2TripleClicked;
  1232. }
  1233. if ((me.ButtonState & NetEvents.MouseButtonState.Button3Pressed) != 0) {
  1234. mouseFlag |= MouseFlags.Button3Pressed;
  1235. }
  1236. if ((me.ButtonState & NetEvents.MouseButtonState.Button3Released) != 0) {
  1237. mouseFlag |= MouseFlags.Button3Released;
  1238. }
  1239. if ((me.ButtonState & NetEvents.MouseButtonState.Button3Clicked) != 0) {
  1240. mouseFlag |= MouseFlags.Button3Clicked;
  1241. }
  1242. if ((me.ButtonState & NetEvents.MouseButtonState.Button3DoubleClicked) != 0) {
  1243. mouseFlag |= MouseFlags.Button3DoubleClicked;
  1244. }
  1245. if ((me.ButtonState & NetEvents.MouseButtonState.Button3TripleClicked) != 0) {
  1246. mouseFlag |= MouseFlags.Button3TripleClicked;
  1247. }
  1248. if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledUp) != 0) {
  1249. mouseFlag |= MouseFlags.WheeledUp;
  1250. }
  1251. if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledDown) != 0) {
  1252. mouseFlag |= MouseFlags.WheeledDown;
  1253. }
  1254. if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledLeft) != 0) {
  1255. mouseFlag |= MouseFlags.WheeledLeft;
  1256. }
  1257. if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledRight) != 0) {
  1258. mouseFlag |= MouseFlags.WheeledRight;
  1259. }
  1260. if ((me.ButtonState & NetEvents.MouseButtonState.Button4Pressed) != 0) {
  1261. mouseFlag |= MouseFlags.Button4Pressed;
  1262. }
  1263. if ((me.ButtonState & NetEvents.MouseButtonState.Button4Released) != 0) {
  1264. mouseFlag |= MouseFlags.Button4Released;
  1265. }
  1266. if ((me.ButtonState & NetEvents.MouseButtonState.Button4Clicked) != 0) {
  1267. mouseFlag |= MouseFlags.Button4Clicked;
  1268. }
  1269. if ((me.ButtonState & NetEvents.MouseButtonState.Button4DoubleClicked) != 0) {
  1270. mouseFlag |= MouseFlags.Button4DoubleClicked;
  1271. }
  1272. if ((me.ButtonState & NetEvents.MouseButtonState.Button4TripleClicked) != 0) {
  1273. mouseFlag |= MouseFlags.Button4TripleClicked;
  1274. }
  1275. if ((me.ButtonState & NetEvents.MouseButtonState.ReportMousePosition) != 0) {
  1276. mouseFlag |= MouseFlags.ReportMousePosition;
  1277. }
  1278. if ((me.ButtonState & NetEvents.MouseButtonState.ButtonShift) != 0) {
  1279. mouseFlag |= MouseFlags.ButtonShift;
  1280. }
  1281. if ((me.ButtonState & NetEvents.MouseButtonState.ButtonCtrl) != 0) {
  1282. mouseFlag |= MouseFlags.ButtonCtrl;
  1283. }
  1284. if ((me.ButtonState & NetEvents.MouseButtonState.ButtonAlt) != 0) {
  1285. mouseFlag |= MouseFlags.ButtonAlt;
  1286. }
  1287. return new MouseEvent () {
  1288. X = me.Position.X,
  1289. Y = me.Position.Y,
  1290. Flags = mouseFlag
  1291. };
  1292. }
  1293. /// <inheritdoc/>
  1294. public override bool GetCursorVisibility (out CursorVisibility visibility)
  1295. {
  1296. visibility = savedCursorVisibility ?? CursorVisibility.Default;
  1297. return visibility == CursorVisibility.Default;
  1298. }
  1299. /// <inheritdoc/>
  1300. public override bool SetCursorVisibility (CursorVisibility visibility)
  1301. {
  1302. savedCursorVisibility = visibility;
  1303. return Console.CursorVisible = visibility == CursorVisibility.Default;
  1304. }
  1305. /// <inheritdoc/>
  1306. public override bool EnsureCursorVisibility ()
  1307. {
  1308. if (!(ccol >= 0 && crow >= 0 && ccol < Cols && crow < Rows)) {
  1309. GetCursorVisibility (out CursorVisibility cursorVisibility);
  1310. savedCursorVisibility = cursorVisibility;
  1311. SetCursorVisibility (CursorVisibility.Invisible);
  1312. return false;
  1313. }
  1314. SetCursorVisibility (savedCursorVisibility ?? CursorVisibility.Default);
  1315. return savedCursorVisibility == CursorVisibility.Default;
  1316. }
  1317. public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control)
  1318. {
  1319. NetEvents.InputResult input = new NetEvents.InputResult ();
  1320. input.EventType = NetEvents.EventType.Key;
  1321. input.ConsoleKeyInfo = new ConsoleKeyInfo (keyChar, key, shift, alt, control);
  1322. try {
  1323. ProcessInput (input);
  1324. } catch (OverflowException) { }
  1325. }
  1326. public override bool GetColors (int value, out Color foreground, out Color background)
  1327. {
  1328. bool hasColor = false;
  1329. foreground = default;
  1330. background = default;
  1331. IEnumerable<int> values = Enum.GetValues (typeof (ConsoleColor))
  1332. .OfType<ConsoleColor> ()
  1333. .Select (s => (int)s);
  1334. if (values.Contains (value & 0xffff)) {
  1335. hasColor = true;
  1336. background = (Color)(ConsoleColor)(value & 0xffff);
  1337. }
  1338. if (values.Contains ((value >> 16) & 0xffff)) {
  1339. hasColor = true;
  1340. foreground = (Color)(ConsoleColor)((value >> 16) & 0xffff);
  1341. }
  1342. return hasColor;
  1343. }
  1344. #region Unused
  1345. public override void SetColors (ConsoleColor foreground, ConsoleColor background)
  1346. {
  1347. }
  1348. public override void SetColors (short foregroundColorId, short backgroundColorId)
  1349. {
  1350. }
  1351. public override void CookMouse ()
  1352. {
  1353. }
  1354. public override void UncookMouse ()
  1355. {
  1356. }
  1357. #endregion
  1358. //
  1359. // These are for the .NET driver, but running natively on Windows, wont run
  1360. // on the Mono emulation
  1361. //
  1362. }
  1363. /// <summary>
  1364. /// Mainloop intended to be used with the .NET System.Console API, and can
  1365. /// be used on Windows and Unix, it is cross platform but lacks things like
  1366. /// file descriptor monitoring.
  1367. /// </summary>
  1368. /// <remarks>
  1369. /// This implementation is used for NetDriver.
  1370. /// </remarks>
  1371. internal class NetMainLoop : IMainLoopDriver {
  1372. ManualResetEventSlim keyReady = new ManualResetEventSlim (false);
  1373. ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
  1374. Queue<NetEvents.InputResult?> inputResult = new Queue<NetEvents.InputResult?> ();
  1375. MainLoop mainLoop;
  1376. CancellationTokenSource tokenSource = new CancellationTokenSource ();
  1377. internal NetEvents netEvents;
  1378. /// <summary>
  1379. /// Invoked when a Key is pressed.
  1380. /// </summary>
  1381. public Action<NetEvents.InputResult> ProcessInput;
  1382. /// <summary>
  1383. /// Initializes the class with the console driver.
  1384. /// </summary>
  1385. /// <remarks>
  1386. /// Passing a consoleDriver is provided to capture windows resizing.
  1387. /// </remarks>
  1388. /// <param name="consoleDriver">The console driver used by this Net main loop.</param>
  1389. public NetMainLoop (ConsoleDriver consoleDriver = null)
  1390. {
  1391. if (consoleDriver == null) {
  1392. throw new ArgumentNullException ("Console driver instance must be provided.");
  1393. }
  1394. netEvents = new NetEvents (consoleDriver);
  1395. }
  1396. void NetInputHandler ()
  1397. {
  1398. while (true) {
  1399. waitForProbe.Wait ();
  1400. waitForProbe.Reset ();
  1401. if (inputResult.Count == 0) {
  1402. inputResult.Enqueue (netEvents.ReadConsoleInput ());
  1403. }
  1404. try {
  1405. while (inputResult.Peek () == null) {
  1406. inputResult.Dequeue ();
  1407. }
  1408. if (inputResult.Count > 0) {
  1409. keyReady.Set ();
  1410. }
  1411. } catch (InvalidOperationException) { }
  1412. }
  1413. }
  1414. void IMainLoopDriver.Setup (MainLoop mainLoop)
  1415. {
  1416. this.mainLoop = mainLoop;
  1417. Task.Run (NetInputHandler);
  1418. }
  1419. void IMainLoopDriver.Wakeup ()
  1420. {
  1421. keyReady.Set ();
  1422. }
  1423. bool IMainLoopDriver.EventsPending (bool wait)
  1424. {
  1425. waitForProbe.Set ();
  1426. if (CheckTimers (wait, out var waitTimeout)) {
  1427. return true;
  1428. }
  1429. try {
  1430. if (!tokenSource.IsCancellationRequested) {
  1431. keyReady.Wait (waitTimeout, tokenSource.Token);
  1432. }
  1433. } catch (OperationCanceledException) {
  1434. return true;
  1435. } finally {
  1436. keyReady.Reset ();
  1437. }
  1438. if (!tokenSource.IsCancellationRequested) {
  1439. return inputResult.Count > 0 || CheckTimers (wait, out _);
  1440. }
  1441. tokenSource.Dispose ();
  1442. tokenSource = new CancellationTokenSource ();
  1443. return true;
  1444. }
  1445. bool CheckTimers (bool wait, out int waitTimeout)
  1446. {
  1447. long now = DateTime.UtcNow.Ticks;
  1448. if (mainLoop.timeouts.Count > 0) {
  1449. waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond);
  1450. if (waitTimeout < 0)
  1451. return true;
  1452. } else {
  1453. waitTimeout = -1;
  1454. }
  1455. if (!wait)
  1456. waitTimeout = 0;
  1457. int ic;
  1458. lock (mainLoop.idleHandlers) {
  1459. ic = mainLoop.idleHandlers.Count;
  1460. }
  1461. return ic > 0;
  1462. }
  1463. void IMainLoopDriver.Iteration ()
  1464. {
  1465. while (inputResult.Count > 0) {
  1466. ProcessInput?.Invoke (inputResult.Dequeue ().Value);
  1467. }
  1468. }
  1469. }
  1470. }