NetDriver.cs 48 KB

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