NetDriver.cs 47 KB

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