NetDriver.cs 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641
  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. public NetWinVTConsole NetWinConsole { get; }
  571. public bool IsWinPlatform { get; }
  572. public override IClipboard Clipboard { get; }
  573. public override int [,,] Contents => contents;
  574. int largestBufferHeight;
  575. public NetDriver ()
  576. {
  577. var p = Environment.OSVersion.Platform;
  578. if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
  579. IsWinPlatform = true;
  580. NetWinConsole = new NetWinVTConsole ();
  581. }
  582. if (IsWinPlatform) {
  583. Clipboard = new WindowsClipboard ();
  584. } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
  585. Clipboard = new MacOSXClipboard ();
  586. } else {
  587. if (CursesDriver.Is_WSL_Platform ()) {
  588. Clipboard = new WSLClipboard ();
  589. } else {
  590. Clipboard = new CursesClipboard ();
  591. }
  592. }
  593. }
  594. // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
  595. int [,,] contents;
  596. bool [] dirtyLine;
  597. static bool sync = false;
  598. // Current row, and current col, tracked by Move/AddCh only
  599. int ccol, crow;
  600. public override void Move (int col, int row)
  601. {
  602. ccol = col;
  603. crow = row;
  604. }
  605. public override void AddRune (Rune rune)
  606. {
  607. if (contents.Length != Rows * Cols * 3) {
  608. return;
  609. }
  610. rune = MakePrintable (rune);
  611. var runeWidth = Rune.ColumnWidth (rune);
  612. var validClip = IsValidContent (ccol, crow, Clip);
  613. if (validClip) {
  614. if (runeWidth == 0 && ccol > 0) {
  615. var r = contents [crow, ccol - 1, 0];
  616. var s = new string (new char [] { (char)r, (char)rune });
  617. string sn;
  618. if (!s.IsNormalized ()) {
  619. sn = s.Normalize ();
  620. } else {
  621. sn = s;
  622. }
  623. var c = sn [0];
  624. contents [crow, ccol - 1, 0] = c;
  625. contents [crow, ccol - 1, 1] = CurrentAttribute;
  626. contents [crow, ccol - 1, 2] = 1;
  627. } else {
  628. if (runeWidth < 2 && ccol > 0
  629. && Rune.ColumnWidth ((char)contents [crow, ccol - 1, 0]) > 1) {
  630. contents [crow, ccol - 1, 0] = (int)(uint)' ';
  631. } else if (runeWidth < 2 && ccol <= Clip.Right - 1
  632. && Rune.ColumnWidth ((char)contents [crow, ccol, 0]) > 1) {
  633. contents [crow, ccol + 1, 0] = (int)(uint)' ';
  634. contents [crow, ccol + 1, 2] = 1;
  635. }
  636. if (runeWidth > 1 && ccol == Clip.Right - 1) {
  637. contents [crow, ccol, 0] = (int)(uint)' ';
  638. } else {
  639. contents [crow, ccol, 0] = (int)(uint)rune;
  640. }
  641. contents [crow, ccol, 1] = CurrentAttribute;
  642. contents [crow, ccol, 2] = 1;
  643. }
  644. dirtyLine [crow] = true;
  645. }
  646. if (runeWidth < 0 || runeWidth > 0) {
  647. ccol++;
  648. }
  649. if (runeWidth > 1) {
  650. if (validClip && ccol < Clip.Right) {
  651. contents [crow, ccol, 1] = CurrentAttribute;
  652. contents [crow, ccol, 2] = 0;
  653. }
  654. ccol++;
  655. }
  656. if (sync) {
  657. UpdateScreen ();
  658. }
  659. }
  660. public override void AddStr (ustring str)
  661. {
  662. foreach (var rune in str)
  663. AddRune (rune);
  664. }
  665. public override void End ()
  666. {
  667. mainLoop.netEvents.StopTasks ();
  668. if (IsWinPlatform) {
  669. NetWinConsole.Cleanup ();
  670. }
  671. StopReportingMouseMoves ();
  672. Console.ResetColor ();
  673. //Disable alternative screen buffer.
  674. Console.Out.Write ("\x1b[?1049l");
  675. //Set cursor key to cursor.
  676. Console.Out.Write ("\x1b[?25h");
  677. Console.Out.Close ();
  678. }
  679. public override Attribute MakeColor (Color foreground, Color background)
  680. {
  681. return MakeColor ((ConsoleColor)foreground, (ConsoleColor)background);
  682. }
  683. static Attribute MakeColor (ConsoleColor f, ConsoleColor b)
  684. {
  685. // Encode the colors into the int value.
  686. return new Attribute (
  687. value: ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff),
  688. foreground: (Color)f,
  689. background: (Color)b
  690. );
  691. }
  692. public override void Init (Action terminalResized)
  693. {
  694. TerminalResized = terminalResized;
  695. //Enable alternative screen buffer.
  696. Console.Out.Write ("\x1b[?1049h");
  697. //Set cursor key to application.
  698. Console.Out.Write ("\x1b[?25l");
  699. Console.TreatControlCAsInput = true;
  700. if (EnableConsoleScrolling) {
  701. largestBufferHeight = Console.BufferHeight;
  702. } else {
  703. largestBufferHeight = Console.WindowHeight;
  704. }
  705. cols = Console.WindowWidth;
  706. rows = largestBufferHeight;
  707. CurrentAttribute = MakeColor (Color.White, Color.Black);
  708. InitalizeColorSchemes ();
  709. CurrentAttribute = MakeColor (Color.White, Color.Black);
  710. InitalizeColorSchemes ();
  711. ResizeScreen ();
  712. UpdateOffScreen ();
  713. StartReportingMouseMoves ();
  714. }
  715. public override void ResizeScreen ()
  716. {
  717. if (!EnableConsoleScrolling) {
  718. if (Console.WindowHeight > 0) {
  719. // Not supported on Unix.
  720. if (IsWinPlatform) {
  721. // Can raise an exception while is still resizing.
  722. try {
  723. #pragma warning disable CA1416
  724. Console.CursorTop = 0;
  725. Console.CursorLeft = 0;
  726. Console.WindowTop = 0;
  727. Console.WindowLeft = 0;
  728. if (Console.WindowHeight > Rows) {
  729. Console.SetWindowSize (Cols, Rows);
  730. }
  731. Console.SetBufferSize (Cols, Rows);
  732. #pragma warning restore CA1416
  733. } catch (System.IO.IOException) {
  734. setClip ();
  735. } catch (ArgumentOutOfRangeException) {
  736. setClip ();
  737. }
  738. } else {
  739. Console.Out.Write ($"\x1b[8;{Rows};{Cols}t");
  740. }
  741. }
  742. } else {
  743. if (IsWinPlatform) {
  744. if (Console.WindowHeight > 0) {
  745. // Can raise an exception while is still resizing.
  746. try {
  747. #pragma warning disable CA1416
  748. Console.CursorTop = 0;
  749. Console.CursorLeft = 0;
  750. if (Console.WindowHeight > Rows) {
  751. Console.SetWindowSize (Cols, Rows);
  752. }
  753. Console.SetBufferSize (Cols, Rows);
  754. #pragma warning restore CA1416
  755. } catch (System.IO.IOException) {
  756. setClip ();
  757. } catch (ArgumentOutOfRangeException) {
  758. setClip ();
  759. }
  760. }
  761. } else {
  762. Console.Out.Write ($"\x1b[30;{Rows};{Cols}t");
  763. }
  764. }
  765. setClip ();
  766. void setClip ()
  767. {
  768. Clip = new Rect (0, 0, Cols, Rows);
  769. }
  770. }
  771. public override void UpdateOffScreen ()
  772. {
  773. contents = new int [Rows, Cols, 3];
  774. dirtyLine = new bool [Rows];
  775. lock (contents) {
  776. // Can raise an exception while is still resizing.
  777. try {
  778. for (int row = 0; row < rows; row++) {
  779. for (int c = 0; c < cols; c++) {
  780. contents [row, c, 0] = ' ';
  781. contents [row, c, 1] = (ushort)Colors.TopLevel.Normal;
  782. contents [row, c, 2] = 0;
  783. dirtyLine [row] = true;
  784. }
  785. }
  786. } catch (IndexOutOfRangeException) { }
  787. }
  788. }
  789. public override Attribute MakeAttribute (Color fore, Color back)
  790. {
  791. return MakeColor ((ConsoleColor)fore, (ConsoleColor)back);
  792. }
  793. public override void Refresh ()
  794. {
  795. UpdateScreen ();
  796. UpdateCursor ();
  797. }
  798. public override void UpdateScreen ()
  799. {
  800. if (winChanging || Console.WindowHeight < 1 || contents.Length != Rows * Cols * 3
  801. || (!EnableConsoleScrolling && Rows != Console.WindowHeight)
  802. || (EnableConsoleScrolling && Rows != largestBufferHeight)) {
  803. return;
  804. }
  805. int top = 0;
  806. int left = 0;
  807. int rows = Rows;
  808. int cols = Cols;
  809. System.Text.StringBuilder output = new System.Text.StringBuilder ();
  810. int redrawAttr = -1;
  811. var lastCol = -1;
  812. Console.CursorVisible = false;
  813. for (int row = top; row < rows; row++) {
  814. if (Console.WindowHeight < 1) {
  815. return;
  816. }
  817. if (!dirtyLine [row]) {
  818. continue;
  819. }
  820. if (!SetCursorPosition (0, row)) {
  821. return;
  822. }
  823. dirtyLine [row] = false;
  824. output.Clear ();
  825. for (int col = left; col < cols; col++) {
  826. lastCol = -1;
  827. var outputWidth = 0;
  828. for (; col < cols; col++) {
  829. if (contents [row, col, 2] == 0) {
  830. if (output.Length > 0) {
  831. SetCursorPosition (lastCol, row);
  832. Console.Write (output);
  833. output.Clear ();
  834. lastCol += outputWidth;
  835. outputWidth = 0;
  836. } else if (lastCol == -1) {
  837. lastCol = col;
  838. }
  839. if (lastCol + 1 < cols)
  840. lastCol++;
  841. continue;
  842. }
  843. if (lastCol == -1)
  844. lastCol = col;
  845. var attr = contents [row, col, 1];
  846. if (attr != redrawAttr) {
  847. redrawAttr = attr;
  848. output.Append (WriteAttributes (attr));
  849. }
  850. outputWidth++;
  851. var rune = contents [row, col, 0];
  852. char [] spair;
  853. if (Rune.DecodeSurrogatePair ((uint)rune, out spair)) {
  854. output.Append (spair);
  855. } else {
  856. output.Append ((char)rune);
  857. }
  858. contents [row, col, 2] = 0;
  859. }
  860. }
  861. if (output.Length > 0) {
  862. SetCursorPosition (lastCol, row);
  863. Console.Write (output);
  864. }
  865. }
  866. SetCursorPosition (0, 0);
  867. }
  868. void SetVirtualCursorPosition (int col, int row)
  869. {
  870. Console.Out.Write ($"\x1b[{row + 1};{col + 1}H");
  871. }
  872. System.Text.StringBuilder WriteAttributes (int attr)
  873. {
  874. const string CSI = "\x1b[";
  875. int bg = 0;
  876. int fg = 0;
  877. System.Text.StringBuilder sb = new System.Text.StringBuilder ();
  878. IEnumerable<int> values = Enum.GetValues (typeof (ConsoleColor))
  879. .OfType<ConsoleColor> ()
  880. .Select (s => (int)s);
  881. if (values.Contains (attr & 0xffff)) {
  882. bg = MapColors ((ConsoleColor)(attr & 0xffff), false);
  883. }
  884. if (values.Contains ((attr >> 16) & 0xffff)) {
  885. fg = MapColors ((ConsoleColor)((attr >> 16) & 0xffff));
  886. }
  887. sb.Append ($"{CSI}{bg};{fg}m");
  888. return sb;
  889. }
  890. int MapColors (ConsoleColor color, bool isForeground = true)
  891. {
  892. switch (color) {
  893. case ConsoleColor.Black:
  894. return isForeground ? COLOR_BLACK : COLOR_BLACK + 10;
  895. case ConsoleColor.DarkBlue:
  896. return isForeground ? COLOR_BLUE : COLOR_BLUE + 10;
  897. case ConsoleColor.DarkGreen:
  898. return isForeground ? COLOR_GREEN : COLOR_GREEN + 10;
  899. case ConsoleColor.DarkCyan:
  900. return isForeground ? COLOR_CYAN : COLOR_CYAN + 10;
  901. case ConsoleColor.DarkRed:
  902. return isForeground ? COLOR_RED : COLOR_RED + 10;
  903. case ConsoleColor.DarkMagenta:
  904. return isForeground ? COLOR_MAGENTA : COLOR_MAGENTA + 10;
  905. case ConsoleColor.DarkYellow:
  906. return isForeground ? COLOR_YELLOW : COLOR_YELLOW + 10;
  907. case ConsoleColor.Gray:
  908. return isForeground ? COLOR_WHITE : COLOR_WHITE + 10;
  909. case ConsoleColor.DarkGray:
  910. return isForeground ? COLOR_BRIGHT_BLACK : COLOR_BRIGHT_BLACK + 10;
  911. case ConsoleColor.Blue:
  912. return isForeground ? COLOR_BRIGHT_BLUE : COLOR_BRIGHT_BLUE + 10;
  913. case ConsoleColor.Green:
  914. return isForeground ? COLOR_BRIGHT_GREEN : COLOR_BRIGHT_GREEN + 10;
  915. case ConsoleColor.Cyan:
  916. return isForeground ? COLOR_BRIGHT_CYAN : COLOR_BRIGHT_CYAN + 10;
  917. case ConsoleColor.Red:
  918. return isForeground ? COLOR_BRIGHT_RED : COLOR_BRIGHT_RED + 10;
  919. case ConsoleColor.Magenta:
  920. return isForeground ? COLOR_BRIGHT_MAGENTA : COLOR_BRIGHT_MAGENTA + 10;
  921. case ConsoleColor.Yellow:
  922. return isForeground ? COLOR_BRIGHT_YELLOW : COLOR_BRIGHT_YELLOW + 10;
  923. case ConsoleColor.White:
  924. return isForeground ? COLOR_BRIGHT_WHITE : COLOR_BRIGHT_WHITE + 10;
  925. }
  926. return 0;
  927. }
  928. bool SetCursorPosition (int col, int row)
  929. {
  930. if (IsWinPlatform) {
  931. // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth.
  932. try {
  933. Console.SetCursorPosition (col, row);
  934. return true;
  935. } catch (Exception) {
  936. return false;
  937. }
  938. } else {
  939. SetVirtualCursorPosition (col, row);
  940. return true;
  941. }
  942. }
  943. private void SetWindowPosition (int col, int row)
  944. {
  945. if (IsWinPlatform && EnableConsoleScrolling) {
  946. var winTop = Math.Max (Rows - Console.WindowHeight - row, 0);
  947. winTop = Math.Min (winTop, Rows - Console.WindowHeight + 1);
  948. winTop = Math.Max (winTop, 0);
  949. if (winTop != Console.WindowTop) {
  950. try {
  951. if (!EnsureBufferSize ()) {
  952. return;
  953. }
  954. #pragma warning disable CA1416
  955. Console.SetWindowPosition (col, winTop);
  956. #pragma warning restore CA1416
  957. } catch (System.IO.IOException) {
  958. } catch (System.ArgumentOutOfRangeException) { }
  959. }
  960. }
  961. top = Console.WindowTop;
  962. left = Console.WindowLeft;
  963. }
  964. private bool EnsureBufferSize ()
  965. {
  966. #pragma warning disable CA1416
  967. if (IsWinPlatform && Console.BufferHeight < Rows) {
  968. try {
  969. Console.SetBufferSize (Console.WindowWidth, Rows);
  970. } catch (Exception) {
  971. return false;
  972. }
  973. }
  974. #pragma warning restore CA1416
  975. return true;
  976. }
  977. private CursorVisibility? savedCursorVisibility;
  978. public override void UpdateCursor ()
  979. {
  980. EnsureCursorVisibility ();
  981. //Debug.WriteLine ($"Before - CursorTop: {Console.CursorTop};CursorLeft: {Console.CursorLeft}");
  982. if (ccol >= 0 && ccol < Cols && crow >= 0 && crow < Rows) {
  983. SetCursorPosition (ccol, crow);
  984. SetWindowPosition (0, crow);
  985. }
  986. //Debug.WriteLine ($"WindowTop: {Console.WindowTop};WindowLeft: {Console.WindowLeft}");
  987. //Debug.WriteLine ($"After - CursorTop: {Console.CursorTop};CursorLeft: {Console.CursorLeft}");
  988. }
  989. public override void StartReportingMouseMoves ()
  990. {
  991. Console.Out.Write (EscSeqUtils.EnableMouseEvents);
  992. }
  993. public override void StopReportingMouseMoves ()
  994. {
  995. Console.Out.Write (EscSeqUtils.DisableMouseEvents);
  996. }
  997. public override void Suspend ()
  998. {
  999. }
  1000. public override void SetAttribute (Attribute c)
  1001. {
  1002. base.SetAttribute (c);
  1003. }
  1004. public ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
  1005. {
  1006. if (consoleKeyInfo.Key != ConsoleKey.Packet) {
  1007. return consoleKeyInfo;
  1008. }
  1009. var mod = consoleKeyInfo.Modifiers;
  1010. var shift = (mod & ConsoleModifiers.Shift) != 0;
  1011. var alt = (mod & ConsoleModifiers.Alt) != 0;
  1012. var control = (mod & ConsoleModifiers.Control) != 0;
  1013. var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out uint virtualKey, out _);
  1014. return new ConsoleKeyInfo ((char)keyChar, (ConsoleKey)virtualKey, shift, alt, control);
  1015. }
  1016. Key MapKey (ConsoleKeyInfo keyInfo)
  1017. {
  1018. MapKeyModifiers (keyInfo, (Key)keyInfo.Key);
  1019. switch (keyInfo.Key) {
  1020. case ConsoleKey.Escape:
  1021. return MapKeyModifiers (keyInfo, Key.Esc);
  1022. case ConsoleKey.Tab:
  1023. return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab;
  1024. case ConsoleKey.Home:
  1025. return MapKeyModifiers (keyInfo, Key.Home);
  1026. case ConsoleKey.End:
  1027. return MapKeyModifiers (keyInfo, Key.End);
  1028. case ConsoleKey.LeftArrow:
  1029. return MapKeyModifiers (keyInfo, Key.CursorLeft);
  1030. case ConsoleKey.RightArrow:
  1031. return MapKeyModifiers (keyInfo, Key.CursorRight);
  1032. case ConsoleKey.UpArrow:
  1033. return MapKeyModifiers (keyInfo, Key.CursorUp);
  1034. case ConsoleKey.DownArrow:
  1035. return MapKeyModifiers (keyInfo, Key.CursorDown);
  1036. case ConsoleKey.PageUp:
  1037. return MapKeyModifiers (keyInfo, Key.PageUp);
  1038. case ConsoleKey.PageDown:
  1039. return MapKeyModifiers (keyInfo, Key.PageDown);
  1040. case ConsoleKey.Enter:
  1041. return MapKeyModifiers (keyInfo, Key.Enter);
  1042. case ConsoleKey.Spacebar:
  1043. return MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar);
  1044. case ConsoleKey.Backspace:
  1045. return MapKeyModifiers (keyInfo, Key.Backspace);
  1046. case ConsoleKey.Delete:
  1047. return MapKeyModifiers (keyInfo, Key.DeleteChar);
  1048. case ConsoleKey.Insert:
  1049. return MapKeyModifiers (keyInfo, Key.InsertChar);
  1050. case ConsoleKey.Oem1:
  1051. case ConsoleKey.Oem2:
  1052. case ConsoleKey.Oem3:
  1053. case ConsoleKey.Oem4:
  1054. case ConsoleKey.Oem5:
  1055. case ConsoleKey.Oem6:
  1056. case ConsoleKey.Oem7:
  1057. case ConsoleKey.Oem8:
  1058. case ConsoleKey.Oem102:
  1059. case ConsoleKey.OemPeriod:
  1060. case ConsoleKey.OemComma:
  1061. case ConsoleKey.OemPlus:
  1062. case ConsoleKey.OemMinus:
  1063. return (Key)((uint)keyInfo.KeyChar);
  1064. }
  1065. var key = keyInfo.Key;
  1066. if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
  1067. var delta = key - ConsoleKey.A;
  1068. if (keyInfo.Modifiers == ConsoleModifiers.Control) {
  1069. return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta));
  1070. }
  1071. if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
  1072. return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta));
  1073. }
  1074. if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
  1075. if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) {
  1076. return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta));
  1077. }
  1078. }
  1079. return (Key)((uint)keyInfo.KeyChar);
  1080. }
  1081. if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
  1082. var delta = key - ConsoleKey.D0;
  1083. if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
  1084. return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta));
  1085. }
  1086. if (keyInfo.Modifiers == ConsoleModifiers.Control) {
  1087. return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta));
  1088. }
  1089. if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
  1090. if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) {
  1091. return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
  1092. }
  1093. }
  1094. return (Key)((uint)keyInfo.KeyChar);
  1095. }
  1096. if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) {
  1097. var delta = key - ConsoleKey.F1;
  1098. if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
  1099. return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta));
  1100. }
  1101. return (Key)((uint)Key.F1 + delta);
  1102. }
  1103. if (keyInfo.KeyChar != 0) {
  1104. return MapKeyModifiers (keyInfo, (Key)((uint)keyInfo.KeyChar));
  1105. }
  1106. return (Key)(0xffffffff);
  1107. }
  1108. KeyModifiers keyModifiers;
  1109. Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
  1110. {
  1111. if (keyModifiers == null) {
  1112. keyModifiers = new KeyModifiers ();
  1113. }
  1114. Key keyMod = new Key ();
  1115. if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) {
  1116. keyMod = Key.ShiftMask;
  1117. keyModifiers.Shift = true;
  1118. }
  1119. if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) {
  1120. keyMod |= Key.CtrlMask;
  1121. keyModifiers.Ctrl = true;
  1122. }
  1123. if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) {
  1124. keyMod |= Key.AltMask;
  1125. keyModifiers.Alt = true;
  1126. }
  1127. return keyMod != Key.Null ? keyMod | key : key;
  1128. }
  1129. Action<KeyEvent> keyHandler;
  1130. Action<KeyEvent> keyDownHandler;
  1131. Action<KeyEvent> keyUpHandler;
  1132. Action<MouseEvent> mouseHandler;
  1133. NetMainLoop mainLoop;
  1134. public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
  1135. {
  1136. this.keyHandler = keyHandler;
  1137. this.keyDownHandler = keyDownHandler;
  1138. this.keyUpHandler = keyUpHandler;
  1139. this.mouseHandler = mouseHandler;
  1140. var mLoop = this.mainLoop = mainLoop.Driver as NetMainLoop;
  1141. // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will be simulated to be called.
  1142. mLoop.ProcessInput = (e) => ProcessInput (e);
  1143. // Check if terminal supports requests
  1144. this.mainLoop.netEvents.EscSeqReqProc.Add ("c");
  1145. Console.Out.Write ("\x1b[0c");
  1146. }
  1147. void ProcessInput (NetEvents.InputResult inputEvent)
  1148. {
  1149. switch (inputEvent.EventType) {
  1150. case NetEvents.EventType.Key:
  1151. ConsoleKeyInfo consoleKeyInfo = inputEvent.ConsoleKeyInfo;
  1152. if (consoleKeyInfo.Key == ConsoleKey.Packet) {
  1153. consoleKeyInfo = FromVKPacketToKConsoleKeyInfo (consoleKeyInfo);
  1154. }
  1155. keyModifiers = new KeyModifiers ();
  1156. var map = MapKey (consoleKeyInfo);
  1157. if (map == (Key)0xffffffff) {
  1158. return;
  1159. }
  1160. if (map == Key.Null) {
  1161. keyDownHandler (new KeyEvent (map, keyModifiers));
  1162. keyUpHandler (new KeyEvent (map, keyModifiers));
  1163. } else {
  1164. keyDownHandler (new KeyEvent (map, keyModifiers));
  1165. keyHandler (new KeyEvent (map, keyModifiers));
  1166. keyUpHandler (new KeyEvent (map, keyModifiers));
  1167. }
  1168. break;
  1169. case NetEvents.EventType.Mouse:
  1170. mouseHandler (ToDriverMouse (inputEvent.MouseEvent));
  1171. break;
  1172. case NetEvents.EventType.WindowSize:
  1173. ChangeWin (inputEvent.WindowSizeEvent.Size);
  1174. break;
  1175. case NetEvents.EventType.RequestResponse:
  1176. Application.Top.Data = inputEvent.RequestResponseEvent.ResultTuple;
  1177. break;
  1178. }
  1179. }
  1180. volatile bool winChanging;
  1181. void ChangeWin (Size size)
  1182. {
  1183. winChanging = true;
  1184. if (!EnableConsoleScrolling) {
  1185. largestBufferHeight = Math.Max (size.Height, 0);
  1186. } else {
  1187. largestBufferHeight = Math.Max (size.Height, largestBufferHeight);
  1188. }
  1189. top = 0;
  1190. left = 0;
  1191. cols = size.Width;
  1192. rows = largestBufferHeight;
  1193. ResizeScreen ();
  1194. UpdateOffScreen ();
  1195. winChanging = false;
  1196. TerminalResized?.Invoke ();
  1197. }
  1198. MouseEvent ToDriverMouse (NetEvents.MouseEvent me)
  1199. {
  1200. //System.Diagnostics.Debug.WriteLine ($"X: {me.Position.X}; Y: {me.Position.Y}; ButtonState: {me.ButtonState}");
  1201. MouseFlags mouseFlag = 0;
  1202. if ((me.ButtonState & NetEvents.MouseButtonState.Button1Pressed) != 0) {
  1203. mouseFlag |= MouseFlags.Button1Pressed;
  1204. }
  1205. if ((me.ButtonState & NetEvents.MouseButtonState.Button1Released) != 0) {
  1206. mouseFlag |= MouseFlags.Button1Released;
  1207. }
  1208. if ((me.ButtonState & NetEvents.MouseButtonState.Button1Clicked) != 0) {
  1209. mouseFlag |= MouseFlags.Button1Clicked;
  1210. }
  1211. if ((me.ButtonState & NetEvents.MouseButtonState.Button1DoubleClicked) != 0) {
  1212. mouseFlag |= MouseFlags.Button1DoubleClicked;
  1213. }
  1214. if ((me.ButtonState & NetEvents.MouseButtonState.Button1TripleClicked) != 0) {
  1215. mouseFlag |= MouseFlags.Button1TripleClicked;
  1216. }
  1217. if ((me.ButtonState & NetEvents.MouseButtonState.Button2Pressed) != 0) {
  1218. mouseFlag |= MouseFlags.Button2Pressed;
  1219. }
  1220. if ((me.ButtonState & NetEvents.MouseButtonState.Button2Released) != 0) {
  1221. mouseFlag |= MouseFlags.Button2Released;
  1222. }
  1223. if ((me.ButtonState & NetEvents.MouseButtonState.Button2Clicked) != 0) {
  1224. mouseFlag |= MouseFlags.Button2Clicked;
  1225. }
  1226. if ((me.ButtonState & NetEvents.MouseButtonState.Button2DoubleClicked) != 0) {
  1227. mouseFlag |= MouseFlags.Button2DoubleClicked;
  1228. }
  1229. if ((me.ButtonState & NetEvents.MouseButtonState.Button2TripleClicked) != 0) {
  1230. mouseFlag |= MouseFlags.Button2TripleClicked;
  1231. }
  1232. if ((me.ButtonState & NetEvents.MouseButtonState.Button3Pressed) != 0) {
  1233. mouseFlag |= MouseFlags.Button3Pressed;
  1234. }
  1235. if ((me.ButtonState & NetEvents.MouseButtonState.Button3Released) != 0) {
  1236. mouseFlag |= MouseFlags.Button3Released;
  1237. }
  1238. if ((me.ButtonState & NetEvents.MouseButtonState.Button3Clicked) != 0) {
  1239. mouseFlag |= MouseFlags.Button3Clicked;
  1240. }
  1241. if ((me.ButtonState & NetEvents.MouseButtonState.Button3DoubleClicked) != 0) {
  1242. mouseFlag |= MouseFlags.Button3DoubleClicked;
  1243. }
  1244. if ((me.ButtonState & NetEvents.MouseButtonState.Button3TripleClicked) != 0) {
  1245. mouseFlag |= MouseFlags.Button3TripleClicked;
  1246. }
  1247. if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledUp) != 0) {
  1248. mouseFlag |= MouseFlags.WheeledUp;
  1249. }
  1250. if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledDown) != 0) {
  1251. mouseFlag |= MouseFlags.WheeledDown;
  1252. }
  1253. if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledLeft) != 0) {
  1254. mouseFlag |= MouseFlags.WheeledLeft;
  1255. }
  1256. if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledRight) != 0) {
  1257. mouseFlag |= MouseFlags.WheeledRight;
  1258. }
  1259. if ((me.ButtonState & NetEvents.MouseButtonState.Button4Pressed) != 0) {
  1260. mouseFlag |= MouseFlags.Button4Pressed;
  1261. }
  1262. if ((me.ButtonState & NetEvents.MouseButtonState.Button4Released) != 0) {
  1263. mouseFlag |= MouseFlags.Button4Released;
  1264. }
  1265. if ((me.ButtonState & NetEvents.MouseButtonState.Button4Clicked) != 0) {
  1266. mouseFlag |= MouseFlags.Button4Clicked;
  1267. }
  1268. if ((me.ButtonState & NetEvents.MouseButtonState.Button4DoubleClicked) != 0) {
  1269. mouseFlag |= MouseFlags.Button4DoubleClicked;
  1270. }
  1271. if ((me.ButtonState & NetEvents.MouseButtonState.Button4TripleClicked) != 0) {
  1272. mouseFlag |= MouseFlags.Button4TripleClicked;
  1273. }
  1274. if ((me.ButtonState & NetEvents.MouseButtonState.ReportMousePosition) != 0) {
  1275. mouseFlag |= MouseFlags.ReportMousePosition;
  1276. }
  1277. if ((me.ButtonState & NetEvents.MouseButtonState.ButtonShift) != 0) {
  1278. mouseFlag |= MouseFlags.ButtonShift;
  1279. }
  1280. if ((me.ButtonState & NetEvents.MouseButtonState.ButtonCtrl) != 0) {
  1281. mouseFlag |= MouseFlags.ButtonCtrl;
  1282. }
  1283. if ((me.ButtonState & NetEvents.MouseButtonState.ButtonAlt) != 0) {
  1284. mouseFlag |= MouseFlags.ButtonAlt;
  1285. }
  1286. return new MouseEvent () {
  1287. X = me.Position.X,
  1288. Y = me.Position.Y,
  1289. Flags = mouseFlag
  1290. };
  1291. }
  1292. /// <inheritdoc/>
  1293. public override bool GetCursorVisibility (out CursorVisibility visibility)
  1294. {
  1295. visibility = savedCursorVisibility ?? CursorVisibility.Default;
  1296. return visibility == CursorVisibility.Default;
  1297. }
  1298. /// <inheritdoc/>
  1299. public override bool SetCursorVisibility (CursorVisibility visibility)
  1300. {
  1301. savedCursorVisibility = visibility;
  1302. return Console.CursorVisible = visibility == CursorVisibility.Default;
  1303. }
  1304. /// <inheritdoc/>
  1305. public override bool EnsureCursorVisibility ()
  1306. {
  1307. if (!(ccol >= 0 && crow >= 0 && ccol < Cols && crow < Rows)) {
  1308. GetCursorVisibility (out CursorVisibility cursorVisibility);
  1309. savedCursorVisibility = cursorVisibility;
  1310. SetCursorVisibility (CursorVisibility.Invisible);
  1311. return false;
  1312. }
  1313. SetCursorVisibility (savedCursorVisibility ?? CursorVisibility.Default);
  1314. return savedCursorVisibility == CursorVisibility.Default;
  1315. }
  1316. public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control)
  1317. {
  1318. NetEvents.InputResult input = new NetEvents.InputResult ();
  1319. input.EventType = NetEvents.EventType.Key;
  1320. input.ConsoleKeyInfo = new ConsoleKeyInfo (keyChar, key, shift, alt, control);
  1321. try {
  1322. ProcessInput (input);
  1323. } catch (OverflowException) { }
  1324. }
  1325. public override bool GetColors (int value, out Color foreground, out Color background)
  1326. {
  1327. bool hasColor = false;
  1328. foreground = default;
  1329. background = default;
  1330. IEnumerable<int> values = Enum.GetValues (typeof (ConsoleColor))
  1331. .OfType<ConsoleColor> ()
  1332. .Select (s => (int)s);
  1333. if (values.Contains (value & 0xffff)) {
  1334. hasColor = true;
  1335. background = (Color)(ConsoleColor)(value & 0xffff);
  1336. }
  1337. if (values.Contains ((value >> 16) & 0xffff)) {
  1338. hasColor = true;
  1339. foreground = (Color)(ConsoleColor)((value >> 16) & 0xffff);
  1340. }
  1341. return hasColor;
  1342. }
  1343. #region Unused
  1344. public override void SetColors (ConsoleColor foreground, ConsoleColor background)
  1345. {
  1346. }
  1347. public override void SetColors (short foregroundColorId, short backgroundColorId)
  1348. {
  1349. }
  1350. public override void CookMouse ()
  1351. {
  1352. }
  1353. public override void UncookMouse ()
  1354. {
  1355. }
  1356. #endregion
  1357. //
  1358. // These are for the .NET driver, but running natively on Windows, wont run
  1359. // on the Mono emulation
  1360. //
  1361. }
  1362. /// <summary>
  1363. /// Mainloop intended to be used with the .NET System.Console API, and can
  1364. /// be used on Windows and Unix, it is cross platform but lacks things like
  1365. /// file descriptor monitoring.
  1366. /// </summary>
  1367. /// <remarks>
  1368. /// This implementation is used for NetDriver.
  1369. /// </remarks>
  1370. internal class NetMainLoop : IMainLoopDriver {
  1371. ManualResetEventSlim keyReady = new ManualResetEventSlim (false);
  1372. ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
  1373. Queue<NetEvents.InputResult?> inputResult = new Queue<NetEvents.InputResult?> ();
  1374. MainLoop mainLoop;
  1375. CancellationTokenSource tokenSource = new CancellationTokenSource ();
  1376. internal NetEvents netEvents;
  1377. /// <summary>
  1378. /// Invoked when a Key is pressed.
  1379. /// </summary>
  1380. public Action<NetEvents.InputResult> ProcessInput;
  1381. /// <summary>
  1382. /// Initializes the class with the console driver.
  1383. /// </summary>
  1384. /// <remarks>
  1385. /// Passing a consoleDriver is provided to capture windows resizing.
  1386. /// </remarks>
  1387. /// <param name="consoleDriver">The console driver used by this Net main loop.</param>
  1388. public NetMainLoop (ConsoleDriver consoleDriver = null)
  1389. {
  1390. if (consoleDriver == null) {
  1391. throw new ArgumentNullException ("Console driver instance must be provided.");
  1392. }
  1393. netEvents = new NetEvents (consoleDriver);
  1394. }
  1395. void NetInputHandler ()
  1396. {
  1397. while (true) {
  1398. waitForProbe.Wait ();
  1399. waitForProbe.Reset ();
  1400. if (inputResult.Count == 0) {
  1401. inputResult.Enqueue (netEvents.ReadConsoleInput ());
  1402. }
  1403. try {
  1404. while (inputResult.Peek () == null) {
  1405. inputResult.Dequeue ();
  1406. }
  1407. if (inputResult.Count > 0) {
  1408. keyReady.Set ();
  1409. }
  1410. } catch (InvalidOperationException) { }
  1411. }
  1412. }
  1413. void IMainLoopDriver.Setup (MainLoop mainLoop)
  1414. {
  1415. this.mainLoop = mainLoop;
  1416. Task.Run (NetInputHandler);
  1417. }
  1418. void IMainLoopDriver.Wakeup ()
  1419. {
  1420. keyReady.Set ();
  1421. }
  1422. bool IMainLoopDriver.EventsPending (bool wait)
  1423. {
  1424. waitForProbe.Set ();
  1425. if (CheckTimers (wait, out var waitTimeout)) {
  1426. return true;
  1427. }
  1428. try {
  1429. if (!tokenSource.IsCancellationRequested) {
  1430. keyReady.Wait (waitTimeout, tokenSource.Token);
  1431. }
  1432. } catch (OperationCanceledException) {
  1433. return true;
  1434. } finally {
  1435. keyReady.Reset ();
  1436. }
  1437. if (!tokenSource.IsCancellationRequested) {
  1438. return inputResult.Count > 0 || CheckTimers (wait, out _);
  1439. }
  1440. tokenSource.Dispose ();
  1441. tokenSource = new CancellationTokenSource ();
  1442. return true;
  1443. }
  1444. bool CheckTimers (bool wait, out int waitTimeout)
  1445. {
  1446. long now = DateTime.UtcNow.Ticks;
  1447. if (mainLoop.timeouts.Count > 0) {
  1448. waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond);
  1449. if (waitTimeout < 0)
  1450. return true;
  1451. } else {
  1452. waitTimeout = -1;
  1453. }
  1454. if (!wait)
  1455. waitTimeout = 0;
  1456. int ic;
  1457. lock (mainLoop.idleHandlers) {
  1458. ic = mainLoop.idleHandlers.Count;
  1459. }
  1460. return ic > 0;
  1461. }
  1462. void IMainLoopDriver.Iteration ()
  1463. {
  1464. while (inputResult.Count > 0) {
  1465. ProcessInput?.Invoke (inputResult.Dequeue ().Value);
  1466. }
  1467. }
  1468. }
  1469. }