WindowsDriver.cs 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301
  1. //
  2. // WindowsDriver.cs: Windows specific driver
  3. //
  4. // Authors:
  5. // Miguel de Icaza ([email protected])
  6. // Nick Van Dyck ([email protected])
  7. //
  8. // Copyright (c) 2018
  9. //
  10. // Permission is hereby granted, free of charge, to any person obtaining a copy
  11. // of this software and associated documentation files (the "Software"), to deal
  12. // in the Software without restriction, including without limitation the rights
  13. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  14. // copies of the Software, and to permit persons to whom the Software is
  15. // furnished to do so, subject to the following conditions:
  16. //
  17. // The above copyright notice and this permission notice shall be included in all
  18. // copies or substantial portions of the Software.
  19. //
  20. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  21. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  22. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  23. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  24. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  25. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  26. // SOFTWARE.
  27. //
  28. using NStack;
  29. using System;
  30. using System.Runtime.InteropServices;
  31. using System.Threading;
  32. using System.Threading.Tasks;
  33. namespace Terminal.Gui {
  34. internal class WindowsConsole {
  35. public const int STD_OUTPUT_HANDLE = -11;
  36. public const int STD_INPUT_HANDLE = -10;
  37. public const int STD_ERROR_HANDLE = -12;
  38. internal IntPtr InputHandle, OutputHandle;
  39. IntPtr ScreenBuffer;
  40. uint originalConsoleMode;
  41. public WindowsConsole ()
  42. {
  43. InputHandle = GetStdHandle (STD_INPUT_HANDLE);
  44. OutputHandle = GetStdHandle (STD_OUTPUT_HANDLE);
  45. originalConsoleMode = ConsoleMode;
  46. var newConsoleMode = originalConsoleMode;
  47. newConsoleMode |= (uint)(ConsoleModes.EnableMouseInput | ConsoleModes.EnableExtendedFlags);
  48. newConsoleMode &= ~(uint)ConsoleModes.EnableQuickEditMode;
  49. newConsoleMode &= ~(uint)ConsoleModes.EnableProcessedInput;
  50. ConsoleMode = newConsoleMode;
  51. }
  52. public CharInfo [] OriginalStdOutChars;
  53. public bool WriteToConsole (CharInfo [] charInfoBuffer, Coord coords, SmallRect window)
  54. {
  55. if (ScreenBuffer == IntPtr.Zero) {
  56. ScreenBuffer = CreateConsoleScreenBuffer (
  57. DesiredAccess.GenericRead | DesiredAccess.GenericWrite,
  58. ShareMode.FileShareRead | ShareMode.FileShareWrite,
  59. IntPtr.Zero,
  60. 1,
  61. IntPtr.Zero
  62. );
  63. if (ScreenBuffer == INVALID_HANDLE_VALUE) {
  64. var err = Marshal.GetLastWin32Error ();
  65. if (err != 0)
  66. throw new System.ComponentModel.Win32Exception (err);
  67. }
  68. if (!SetConsoleActiveScreenBuffer (ScreenBuffer)) {
  69. var err = Marshal.GetLastWin32Error ();
  70. throw new System.ComponentModel.Win32Exception (err);
  71. }
  72. OriginalStdOutChars = new CharInfo [Console.WindowHeight * Console.WindowWidth];
  73. ReadConsoleOutput (OutputHandle, OriginalStdOutChars, coords, new Coord () { X = 0, Y = 0 }, ref window);
  74. }
  75. return WriteConsoleOutput (ScreenBuffer, charInfoBuffer, coords, new Coord () { X = window.Left, Y = window.Top }, ref window);
  76. }
  77. public bool SetCursorPosition (Coord position)
  78. {
  79. return SetConsoleCursorPosition (ScreenBuffer, position);
  80. }
  81. public void Cleanup ()
  82. {
  83. ConsoleMode = originalConsoleMode;
  84. //ContinueListeningForConsoleEvents = false;
  85. if (!SetConsoleActiveScreenBuffer (OutputHandle)) {
  86. var err = Marshal.GetLastWin32Error ();
  87. Console.WriteLine ("Error: {0}", err);
  88. }
  89. if (ScreenBuffer != IntPtr.Zero)
  90. CloseHandle (ScreenBuffer);
  91. ScreenBuffer = IntPtr.Zero;
  92. }
  93. //bool ContinueListeningForConsoleEvents = true;
  94. public uint ConsoleMode {
  95. get {
  96. uint v;
  97. GetConsoleMode (InputHandle, out v);
  98. return v;
  99. }
  100. set {
  101. SetConsoleMode (InputHandle, value);
  102. }
  103. }
  104. public ConsoleDriver.ConsoleFont GetFont ()
  105. {
  106. ConsoleDriver.ConsoleFont cf = null;
  107. CONSOLE_FONT_INFO_EX fontInfoEx = new CONSOLE_FONT_INFO_EX ();
  108. fontInfoEx.cbSize = Marshal.SizeOf (typeof (CONSOLE_FONT_INFO_EX));
  109. if (GetCurrentConsoleFontEx(OutputHandle, false, ref fontInfoEx)) {
  110. cf = new ConsoleDriver.ConsoleFont ();
  111. cf.FaceName = fontInfoEx.FaceName;
  112. cf.Size = new Size (fontInfoEx.FontWidth, fontInfoEx.FontHeight);
  113. cf.Weight = fontInfoEx.FontWeight;
  114. }
  115. var err = Marshal.GetLastWin32Error ();
  116. if (err != 0)
  117. throw new System.ComponentModel.Win32Exception (err);
  118. return cf;
  119. }
  120. [Flags]
  121. public enum ConsoleModes : uint {
  122. EnableProcessedInput = 1,
  123. EnableMouseInput = 16,
  124. EnableQuickEditMode = 64,
  125. EnableExtendedFlags = 128,
  126. }
  127. [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)]
  128. public struct KeyEventRecord {
  129. [FieldOffset (0), MarshalAs (UnmanagedType.Bool)]
  130. public bool bKeyDown;
  131. [FieldOffset (4), MarshalAs (UnmanagedType.U2)]
  132. public ushort wRepeatCount;
  133. [FieldOffset (6), MarshalAs (UnmanagedType.U2)]
  134. public ushort wVirtualKeyCode;
  135. [FieldOffset (8), MarshalAs (UnmanagedType.U2)]
  136. public ushort wVirtualScanCode;
  137. [FieldOffset (10)]
  138. public char UnicodeChar;
  139. [FieldOffset (12), MarshalAs (UnmanagedType.U4)]
  140. public ControlKeyState dwControlKeyState;
  141. }
  142. [Flags]
  143. public enum ButtonState {
  144. Button1Pressed = 1,
  145. Button2Pressed = 4,
  146. Button3Pressed = 8,
  147. Button4Pressed = 16,
  148. RightmostButtonPressed = 2,
  149. WheeledUp = unchecked((int)0x780000),
  150. WheeledDown = unchecked((int)0xFF880000),
  151. }
  152. [Flags]
  153. public enum ControlKeyState {
  154. RightAltPressed = 1,
  155. LeftAltPressed = 2,
  156. RightControlPressed = 4,
  157. LeftControlPressed = 8,
  158. ShiftPressed = 16,
  159. NumlockOn = 32,
  160. ScrolllockOn = 64,
  161. CapslockOn = 128,
  162. EnhancedKey = 256
  163. }
  164. [Flags]
  165. public enum EventFlags {
  166. MouseMoved = 1,
  167. DoubleClick = 2,
  168. MouseWheeled = 4,
  169. MouseHorizontalWheeled = 8
  170. }
  171. [StructLayout (LayoutKind.Explicit)]
  172. public struct MouseEventRecord {
  173. [FieldOffset (0)]
  174. public Coordinate MousePosition;
  175. [FieldOffset (4)]
  176. public ButtonState ButtonState;
  177. [FieldOffset (8)]
  178. public ControlKeyState ControlKeyState;
  179. [FieldOffset (12)]
  180. public EventFlags EventFlags;
  181. public override string ToString ()
  182. {
  183. return $"[Mouse({MousePosition},{ButtonState},{ControlKeyState},{EventFlags}";
  184. }
  185. }
  186. [StructLayout (LayoutKind.Sequential)]
  187. public struct Coordinate {
  188. public short X;
  189. public short Y;
  190. public Coordinate (short X, short Y)
  191. {
  192. this.X = X;
  193. this.Y = Y;
  194. }
  195. public override string ToString () => $"({X},{Y})";
  196. };
  197. internal struct WindowBufferSizeRecord {
  198. public Coordinate size;
  199. public WindowBufferSizeRecord (short x, short y)
  200. {
  201. this.size = new Coordinate (x, y);
  202. }
  203. public override string ToString () => $"[WindowBufferSize{size}";
  204. }
  205. [StructLayout (LayoutKind.Sequential)]
  206. public struct MenuEventRecord {
  207. public uint dwCommandId;
  208. }
  209. [StructLayout (LayoutKind.Sequential)]
  210. public struct FocusEventRecord {
  211. public uint bSetFocus;
  212. }
  213. public enum EventType : ushort {
  214. Focus = 0x10,
  215. Key = 0x1,
  216. Menu = 0x8,
  217. Mouse = 2,
  218. WindowBufferSize = 4
  219. }
  220. [StructLayout (LayoutKind.Explicit)]
  221. public struct InputRecord {
  222. [FieldOffset (0)]
  223. public EventType EventType;
  224. [FieldOffset (4)]
  225. public KeyEventRecord KeyEvent;
  226. [FieldOffset (4)]
  227. public MouseEventRecord MouseEvent;
  228. [FieldOffset (4)]
  229. public WindowBufferSizeRecord WindowBufferSizeEvent;
  230. [FieldOffset (4)]
  231. public MenuEventRecord MenuEvent;
  232. [FieldOffset (4)]
  233. public FocusEventRecord FocusEvent;
  234. public override string ToString ()
  235. {
  236. switch (EventType) {
  237. case EventType.Focus:
  238. return FocusEvent.ToString ();
  239. case EventType.Key:
  240. return KeyEvent.ToString ();
  241. case EventType.Menu:
  242. return MenuEvent.ToString ();
  243. case EventType.Mouse:
  244. return MouseEvent.ToString ();
  245. case EventType.WindowBufferSize:
  246. return WindowBufferSizeEvent.ToString ();
  247. default:
  248. return "Unknown event type: " + EventType;
  249. }
  250. }
  251. };
  252. [Flags]
  253. enum ShareMode : uint {
  254. FileShareRead = 1,
  255. FileShareWrite = 2,
  256. }
  257. [Flags]
  258. enum DesiredAccess : uint {
  259. GenericRead = 2147483648,
  260. GenericWrite = 1073741824,
  261. }
  262. [StructLayout (LayoutKind.Sequential)]
  263. public struct ConsoleScreenBufferInfo {
  264. public Coord dwSize;
  265. public Coord dwCursorPosition;
  266. public ushort wAttributes;
  267. public SmallRect srWindow;
  268. public Coord dwMaximumWindowSize;
  269. }
  270. [StructLayout (LayoutKind.Sequential)]
  271. public struct Coord {
  272. public short X;
  273. public short Y;
  274. public Coord (short X, short Y)
  275. {
  276. this.X = X;
  277. this.Y = Y;
  278. }
  279. public override string ToString () => $"({X},{Y})";
  280. };
  281. [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)]
  282. public struct CharUnion {
  283. [FieldOffset (0)] public char UnicodeChar;
  284. [FieldOffset (0)] public byte AsciiChar;
  285. }
  286. [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)]
  287. public struct CharInfo {
  288. [FieldOffset (0)] public CharUnion Char;
  289. [FieldOffset (2)] public ushort Attributes;
  290. }
  291. [StructLayout (LayoutKind.Sequential)]
  292. public struct SmallRect {
  293. public short Left;
  294. public short Top;
  295. public short Right;
  296. public short Bottom;
  297. public static void MakeEmpty (ref SmallRect rect)
  298. {
  299. rect.Left = -1;
  300. }
  301. public static void Update (ref SmallRect rect, short col, short row)
  302. {
  303. if (rect.Left == -1) {
  304. //System.Diagnostics.Debugger.Log (0, "debug", $"damager From Empty {col},{row}\n");
  305. rect.Left = rect.Right = col;
  306. rect.Bottom = rect.Top = row;
  307. return;
  308. }
  309. if (col >= rect.Left && col <= rect.Right && row >= rect.Top && row <= rect.Bottom)
  310. return;
  311. if (col < rect.Left)
  312. rect.Left = col;
  313. if (col > rect.Right)
  314. rect.Right = col;
  315. if (row < rect.Top)
  316. rect.Top = row;
  317. if (row > rect.Bottom)
  318. rect.Bottom = row;
  319. //System.Diagnostics.Debugger.Log (0, "debug", $"Expanding {rect.ToString ()}\n");
  320. }
  321. public override string ToString ()
  322. {
  323. return $"Left={Left},Top={Top},Right={Right},Bottom={Bottom}";
  324. }
  325. }
  326. [DllImport ("kernel32.dll", SetLastError = true)]
  327. static extern IntPtr GetStdHandle (int nStdHandle);
  328. [DllImport ("kernel32.dll", SetLastError = true)]
  329. static extern bool CloseHandle (IntPtr handle);
  330. [DllImport ("kernel32.dll", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode)]
  331. public static extern bool ReadConsoleInput (
  332. IntPtr hConsoleInput,
  333. [Out] InputRecord [] lpBuffer,
  334. uint nLength,
  335. out uint lpNumberOfEventsRead);
  336. [DllImport ("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
  337. static extern bool ReadConsoleOutput (
  338. IntPtr hConsoleOutput,
  339. [Out] CharInfo [] lpBuffer,
  340. Coord dwBufferSize,
  341. Coord dwBufferCoord,
  342. ref SmallRect lpReadRegion
  343. );
  344. [DllImport ("kernel32.dll", EntryPoint = "WriteConsoleOutput", SetLastError = true, CharSet = CharSet.Unicode)]
  345. static extern bool WriteConsoleOutput (
  346. IntPtr hConsoleOutput,
  347. CharInfo [] lpBuffer,
  348. Coord dwBufferSize,
  349. Coord dwBufferCoord,
  350. ref SmallRect lpWriteRegion
  351. );
  352. [DllImport ("kernel32.dll")]
  353. static extern bool SetConsoleCursorPosition (IntPtr hConsoleOutput, Coord dwCursorPosition);
  354. [DllImport ("kernel32.dll")]
  355. static extern bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode);
  356. [DllImport ("kernel32.dll")]
  357. static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode);
  358. [DllImport ("kernel32.dll", SetLastError = true)]
  359. static extern IntPtr CreateConsoleScreenBuffer (
  360. DesiredAccess dwDesiredAccess,
  361. ShareMode dwShareMode,
  362. IntPtr secutiryAttributes,
  363. UInt32 flags,
  364. IntPtr screenBufferData
  365. );
  366. internal static IntPtr INVALID_HANDLE_VALUE = new IntPtr (-1);
  367. [DllImport ("kernel32.dll", SetLastError = true)]
  368. static extern bool SetConsoleActiveScreenBuffer (IntPtr Handle);
  369. [DllImport ("kernel32.dll", SetLastError = true)]
  370. static extern bool GetNumberOfConsoleInputEvents (IntPtr handle, out uint lpcNumberOfEvents);
  371. public uint InputEventCount {
  372. get {
  373. uint v;
  374. GetNumberOfConsoleInputEvents (InputHandle, out v);
  375. return v;
  376. }
  377. }
  378. #if false // See: https://github.com/migueldeicaza/gui.cs/issues/357
  379. [StructLayout (LayoutKind.Sequential)]
  380. public struct SMALL_RECT {
  381. public short Left;
  382. public short Top;
  383. public short Right;
  384. public short Bottom;
  385. public SMALL_RECT (short Left, short Top, short Right, short Bottom)
  386. {
  387. this.Left = Left;
  388. this.Top = Top;
  389. this.Right = Right;
  390. this.Bottom = Bottom;
  391. }
  392. }
  393. [StructLayout (LayoutKind.Sequential)]
  394. public struct CONSOLE_SCREEN_BUFFER_INFO {
  395. public int dwSize;
  396. public int dwCursorPosition;
  397. public short wAttributes;
  398. public SMALL_RECT srWindow;
  399. public int dwMaximumWindowSize;
  400. }
  401. [DllImport ("kernel32.dll", SetLastError = true)]
  402. static extern bool GetConsoleScreenBufferInfo (IntPtr hConsoleOutput, out CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo);
  403. // Theoretically GetConsoleScreenBuffer height should give the console Windoww size
  404. // It does not work, however, and always returns the size the window was initially created at
  405. internal Size GetWindowSize ()
  406. {
  407. var consoleScreenBufferInfo = new CONSOLE_SCREEN_BUFFER_INFO ();
  408. //consoleScreenBufferInfo.dwSize = Marshal.SizeOf (typeof (CONSOLE_SCREEN_BUFFER_INFO));
  409. GetConsoleScreenBufferInfo (OutputHandle, out consoleScreenBufferInfo);
  410. return new Size (consoleScreenBufferInfo.srWindow.Right - consoleScreenBufferInfo.srWindow.Left,
  411. consoleScreenBufferInfo.srWindow.Bottom - consoleScreenBufferInfo.srWindow.Top);
  412. }
  413. #endif
  414. [StructLayout (LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  415. public struct CONSOLE_FONT_INFO_EX {
  416. public int cbSize;
  417. public int FontIndex;
  418. public short FontWidth;
  419. public short FontHeight;
  420. public int FontFamily;
  421. public int FontWeight;
  422. [MarshalAs (UnmanagedType.ByValTStr, SizeConst = 32)]
  423. public string FaceName;
  424. }
  425. [DllImport ("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
  426. extern static bool GetCurrentConsoleFontEx (
  427. IntPtr hConsoleOutput,
  428. bool bMaximumWindow,
  429. [In,Out] ref CONSOLE_FONT_INFO_EX lpConsoleCurrentFont);
  430. }
  431. internal class WindowsDriver : ConsoleDriver, IMainLoopDriver {
  432. static bool sync = false;
  433. ManualResetEventSlim eventReady = new ManualResetEventSlim (false);
  434. ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
  435. MainLoop mainLoop;
  436. WindowsConsole.CharInfo [] OutputBuffer;
  437. int cols, rows;
  438. WindowsConsole winConsole;
  439. WindowsConsole.SmallRect damageRegion;
  440. public override int Cols => cols;
  441. public override int Rows => rows;
  442. public WindowsDriver ()
  443. {
  444. winConsole = new WindowsConsole ();
  445. cols = Console.WindowWidth;
  446. rows = Console.WindowHeight;
  447. WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
  448. ResizeScreen ();
  449. UpdateOffScreen ();
  450. Task.Run ((Action)WindowsInputHandler);
  451. }
  452. [StructLayout (LayoutKind.Sequential)]
  453. public struct ConsoleKeyInfoEx {
  454. public ConsoleKeyInfo consoleKeyInfo;
  455. public bool CapsLock;
  456. public bool NumLock;
  457. public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock)
  458. {
  459. this.consoleKeyInfo = consoleKeyInfo;
  460. CapsLock = capslock;
  461. NumLock = numlock;
  462. }
  463. }
  464. // The records that we keep fetching
  465. WindowsConsole.InputRecord [] result, records = new WindowsConsole.InputRecord [1];
  466. void WindowsInputHandler ()
  467. {
  468. while (true) {
  469. waitForProbe.Wait ();
  470. waitForProbe.Reset ();
  471. uint numberEventsRead = 0;
  472. WindowsConsole.ReadConsoleInput (winConsole.InputHandle, records, 1, out numberEventsRead);
  473. if (numberEventsRead == 0)
  474. result = null;
  475. else
  476. result = records;
  477. eventReady.Set ();
  478. }
  479. }
  480. void IMainLoopDriver.Setup (MainLoop mainLoop)
  481. {
  482. this.mainLoop = mainLoop;
  483. }
  484. void IMainLoopDriver.Wakeup ()
  485. {
  486. tokenSource.Cancel ();
  487. //eventReady.Reset ();
  488. //eventReady.Set ();
  489. }
  490. bool IMainLoopDriver.EventsPending (bool wait)
  491. {
  492. int waitTimeout = 0;
  493. if (CkeckTimeout (wait, ref waitTimeout))
  494. return true;
  495. result = null;
  496. waitForProbe.Set ();
  497. try {
  498. while (result == null) {
  499. if (!tokenSource.IsCancellationRequested)
  500. eventReady.Wait (0, tokenSource.Token);
  501. if (result != null) {
  502. break;
  503. }
  504. if (mainLoop.idleHandlers.Count > 0 || CkeckTimeout (wait, ref waitTimeout)) {
  505. return true;
  506. }
  507. }
  508. } catch (OperationCanceledException) {
  509. return true;
  510. } finally {
  511. eventReady.Reset ();
  512. }
  513. if (!tokenSource.IsCancellationRequested)
  514. return result != null;
  515. tokenSource.Dispose ();
  516. tokenSource = new CancellationTokenSource ();
  517. return true;
  518. }
  519. bool CkeckTimeout (bool wait, ref int waitTimeout)
  520. {
  521. long now = DateTime.UtcNow.Ticks;
  522. if (mainLoop.timeouts.Count > 0) {
  523. waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond);
  524. if (waitTimeout < 0)
  525. return true;
  526. } else {
  527. waitTimeout = -1;
  528. }
  529. if (!wait)
  530. waitTimeout = 0;
  531. return false;
  532. }
  533. Action<KeyEvent> keyHandler;
  534. Action<KeyEvent> keyDownHandler;
  535. Action<KeyEvent> keyUpHandler;
  536. Action<MouseEvent> mouseHandler;
  537. public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
  538. {
  539. this.keyHandler = keyHandler;
  540. this.keyDownHandler = keyDownHandler;
  541. this.keyUpHandler = keyUpHandler;
  542. this.mouseHandler = mouseHandler;
  543. }
  544. void IMainLoopDriver.MainIteration ()
  545. {
  546. if (result == null)
  547. return;
  548. var inputEvent = result [0];
  549. switch (inputEvent.EventType) {
  550. case WindowsConsole.EventType.Key:
  551. var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent));
  552. if (map == (Key)0xffffffff) {
  553. KeyEvent key = new KeyEvent ();
  554. // Shift = VK_SHIFT = 0x10
  555. // Ctrl = VK_CONTROL = 0x11
  556. // Alt = VK_MENU = 0x12
  557. if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.CapslockOn)) {
  558. inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.CapslockOn;
  559. }
  560. if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ScrolllockOn)) {
  561. inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.ScrolllockOn;
  562. }
  563. if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.NumlockOn)) {
  564. inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.NumlockOn;
  565. }
  566. switch (inputEvent.KeyEvent.dwControlKeyState) {
  567. case WindowsConsole.ControlKeyState.RightAltPressed:
  568. case WindowsConsole.ControlKeyState.RightAltPressed |
  569. WindowsConsole.ControlKeyState.LeftControlPressed |
  570. WindowsConsole.ControlKeyState.EnhancedKey:
  571. case WindowsConsole.ControlKeyState.EnhancedKey:
  572. key = new KeyEvent (Key.CtrlMask | Key.AltMask, keyModifiers);
  573. break;
  574. case WindowsConsole.ControlKeyState.LeftAltPressed:
  575. key = new KeyEvent (Key.AltMask, keyModifiers);
  576. break;
  577. case WindowsConsole.ControlKeyState.RightControlPressed:
  578. case WindowsConsole.ControlKeyState.LeftControlPressed:
  579. key = new KeyEvent (Key.CtrlMask, keyModifiers);
  580. break;
  581. case WindowsConsole.ControlKeyState.ShiftPressed:
  582. key = new KeyEvent (Key.ShiftMask, keyModifiers);
  583. break;
  584. case WindowsConsole.ControlKeyState.NumlockOn:
  585. break;
  586. case WindowsConsole.ControlKeyState.ScrolllockOn:
  587. break;
  588. case WindowsConsole.ControlKeyState.CapslockOn:
  589. break;
  590. default:
  591. switch (inputEvent.KeyEvent.wVirtualKeyCode) {
  592. case 0x10:
  593. key = new KeyEvent (Key.ShiftMask, keyModifiers);
  594. break;
  595. case 0x11:
  596. key = new KeyEvent (Key.CtrlMask, keyModifiers);
  597. break;
  598. case 0x12:
  599. key = new KeyEvent (Key.AltMask, keyModifiers);
  600. break;
  601. default:
  602. key = new KeyEvent (Key.Unknown, keyModifiers);
  603. break;
  604. }
  605. break;
  606. }
  607. if (inputEvent.KeyEvent.bKeyDown)
  608. keyDownHandler (key);
  609. else
  610. keyUpHandler (key);
  611. } else {
  612. if (inputEvent.KeyEvent.bKeyDown) {
  613. // Key Down - Fire KeyDown Event and KeyStroke (ProcessKey) Event
  614. keyDownHandler (new KeyEvent (map, keyModifiers));
  615. keyHandler (new KeyEvent (map, keyModifiers));
  616. } else {
  617. keyUpHandler (new KeyEvent (map, keyModifiers));
  618. }
  619. }
  620. if (!inputEvent.KeyEvent.bKeyDown) {
  621. keyModifiers = null;
  622. }
  623. break;
  624. case WindowsConsole.EventType.Mouse:
  625. mouseHandler (ToDriverMouse (inputEvent.MouseEvent));
  626. if (IsButtonReleased)
  627. mouseHandler (ToDriverMouse (inputEvent.MouseEvent));
  628. break;
  629. case WindowsConsole.EventType.WindowBufferSize:
  630. cols = inputEvent.WindowBufferSizeEvent.size.X;
  631. rows = inputEvent.WindowBufferSizeEvent.size.Y;
  632. ResizeScreen ();
  633. UpdateOffScreen ();
  634. TerminalResized?.Invoke ();
  635. break;
  636. case WindowsConsole.EventType.Focus:
  637. break;
  638. default:
  639. break;
  640. }
  641. result = null;
  642. }
  643. WindowsConsole.ButtonState? LastMouseButtonPressed = null;
  644. bool IsButtonPressed = false;
  645. bool IsButtonReleased = false;
  646. bool IsButtonDoubleClicked = false;
  647. Point point;
  648. MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent)
  649. {
  650. MouseFlags mouseFlag = MouseFlags.AllEvents;
  651. if (IsButtonDoubleClicked) {
  652. Application.MainLoop.AddIdle (() => {
  653. ProcessButtonDoubleClickedAsync ().ConfigureAwait (false);
  654. return false;
  655. });
  656. }
  657. // The ButtonState member of the MouseEvent structure has bit corresponding to each mouse button.
  658. // This will tell when a mouse button is pressed. When the button is released this event will
  659. // be fired with it's bit set to 0. So when the button is up ButtonState will be 0.
  660. // To map to the correct driver events we save the last pressed mouse button so we can
  661. // map to the correct clicked event.
  662. if ((LastMouseButtonPressed != null || IsButtonReleased) && mouseEvent.ButtonState != 0) {
  663. LastMouseButtonPressed = null;
  664. IsButtonPressed = false;
  665. IsButtonReleased = false;
  666. }
  667. if ((mouseEvent.ButtonState != 0 && mouseEvent.EventFlags == 0 && LastMouseButtonPressed == null && !IsButtonDoubleClicked) ||
  668. (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved &&
  669. mouseEvent.ButtonState != 0 && !IsButtonReleased && !IsButtonDoubleClicked)) {
  670. switch (mouseEvent.ButtonState) {
  671. case WindowsConsole.ButtonState.Button1Pressed:
  672. mouseFlag = MouseFlags.Button1Pressed;
  673. break;
  674. case WindowsConsole.ButtonState.Button2Pressed:
  675. mouseFlag = MouseFlags.Button2Pressed;
  676. break;
  677. case WindowsConsole.ButtonState.RightmostButtonPressed:
  678. mouseFlag = MouseFlags.Button3Pressed;
  679. break;
  680. }
  681. if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) {
  682. mouseFlag |= MouseFlags.ReportMousePosition;
  683. point = new Point ();
  684. IsButtonReleased = false;
  685. } else {
  686. point = new Point () {
  687. X = mouseEvent.MousePosition.X,
  688. Y = mouseEvent.MousePosition.Y
  689. };
  690. }
  691. LastMouseButtonPressed = mouseEvent.ButtonState;
  692. IsButtonPressed = true;
  693. if ((mouseFlag & MouseFlags.ReportMousePosition) == 0) {
  694. Application.MainLoop.AddIdle (() => {
  695. ProcessContinuousButtonPressedAsync (mouseEvent, mouseFlag).ConfigureAwait (false);
  696. return false;
  697. });
  698. }
  699. } else if ((mouseEvent.EventFlags == 0 || mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) &&
  700. LastMouseButtonPressed != null && !IsButtonReleased && !IsButtonDoubleClicked) {
  701. switch (LastMouseButtonPressed) {
  702. case WindowsConsole.ButtonState.Button1Pressed:
  703. mouseFlag = MouseFlags.Button1Released;
  704. break;
  705. case WindowsConsole.ButtonState.Button2Pressed:
  706. mouseFlag = MouseFlags.Button2Released;
  707. break;
  708. case WindowsConsole.ButtonState.RightmostButtonPressed:
  709. mouseFlag = MouseFlags.Button3Released;
  710. break;
  711. }
  712. IsButtonPressed = false;
  713. IsButtonReleased = true;
  714. } else if ((mouseEvent.EventFlags == 0 || mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) &&
  715. IsButtonReleased) {
  716. var p = new Point () {
  717. X = mouseEvent.MousePosition.X,
  718. Y = mouseEvent.MousePosition.Y
  719. };
  720. //if (p == point) {
  721. switch (LastMouseButtonPressed) {
  722. case WindowsConsole.ButtonState.Button1Pressed:
  723. mouseFlag = MouseFlags.Button1Clicked;
  724. break;
  725. case WindowsConsole.ButtonState.Button2Pressed:
  726. mouseFlag = MouseFlags.Button2Clicked;
  727. break;
  728. case WindowsConsole.ButtonState.RightmostButtonPressed:
  729. mouseFlag = MouseFlags.Button3Clicked;
  730. break;
  731. }
  732. point = new Point () {
  733. X = mouseEvent.MousePosition.X,
  734. Y = mouseEvent.MousePosition.Y
  735. };
  736. //} else {
  737. // mouseFlag = 0;
  738. //}
  739. LastMouseButtonPressed = null;
  740. IsButtonReleased = false;
  741. } else if (mouseEvent.EventFlags.HasFlag (WindowsConsole.EventFlags.DoubleClick)) {
  742. switch (mouseEvent.ButtonState) {
  743. case WindowsConsole.ButtonState.Button1Pressed:
  744. mouseFlag = MouseFlags.Button1DoubleClicked;
  745. break;
  746. case WindowsConsole.ButtonState.Button2Pressed:
  747. mouseFlag = MouseFlags.Button2DoubleClicked;
  748. break;
  749. case WindowsConsole.ButtonState.RightmostButtonPressed:
  750. mouseFlag = MouseFlags.Button3DoubleClicked;
  751. break;
  752. }
  753. IsButtonDoubleClicked = true;
  754. } else if (mouseEvent.EventFlags == 0 && mouseEvent.ButtonState != 0 && IsButtonDoubleClicked) {
  755. switch (mouseEvent.ButtonState) {
  756. case WindowsConsole.ButtonState.Button1Pressed:
  757. mouseFlag = MouseFlags.Button1TripleClicked;
  758. break;
  759. case WindowsConsole.ButtonState.Button2Pressed:
  760. mouseFlag = MouseFlags.Button2TripleClicked;
  761. break;
  762. case WindowsConsole.ButtonState.RightmostButtonPressed:
  763. mouseFlag = MouseFlags.Button3TripleClicked;
  764. break;
  765. }
  766. IsButtonDoubleClicked = false;
  767. } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseWheeled) {
  768. switch (mouseEvent.ButtonState) {
  769. case WindowsConsole.ButtonState.WheeledUp:
  770. mouseFlag = MouseFlags.WheeledUp;
  771. break;
  772. case WindowsConsole.ButtonState.WheeledDown:
  773. mouseFlag = MouseFlags.WheeledDown;
  774. break;
  775. }
  776. } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) {
  777. if (mouseEvent.MousePosition.X != point.X || mouseEvent.MousePosition.Y != point.Y) {
  778. mouseFlag = MouseFlags.ReportMousePosition;
  779. point = new Point ();
  780. } else {
  781. mouseFlag = 0;
  782. }
  783. } else if (mouseEvent.ButtonState == 0 && mouseEvent.EventFlags == 0) {
  784. mouseFlag = 0;
  785. }
  786. mouseFlag = SetControlKeyStates (mouseEvent, mouseFlag);
  787. return new MouseEvent () {
  788. X = mouseEvent.MousePosition.X,
  789. Y = mouseEvent.MousePosition.Y,
  790. Flags = mouseFlag
  791. };
  792. }
  793. async Task ProcessButtonDoubleClickedAsync ()
  794. {
  795. await Task.Delay (200);
  796. IsButtonDoubleClicked = false;
  797. }
  798. async Task ProcessContinuousButtonPressedAsync (WindowsConsole.MouseEventRecord mouseEvent, MouseFlags mouseFlag)
  799. {
  800. while (IsButtonPressed) {
  801. await Task.Delay (200);
  802. var me = new MouseEvent () {
  803. X = mouseEvent.MousePosition.X,
  804. Y = mouseEvent.MousePosition.Y,
  805. Flags = mouseFlag
  806. };
  807. var view = Application.wantContinuousButtonPressedView;
  808. if (view == null) {
  809. break;
  810. }
  811. if (IsButtonPressed && (mouseFlag & MouseFlags.ReportMousePosition) == 0) {
  812. mouseHandler (me);
  813. }
  814. }
  815. }
  816. static MouseFlags SetControlKeyStates (WindowsConsole.MouseEventRecord mouseEvent, MouseFlags mouseFlag)
  817. {
  818. if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightControlPressed) ||
  819. mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftControlPressed))
  820. mouseFlag |= MouseFlags.ButtonCtrl;
  821. if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ShiftPressed))
  822. mouseFlag |= MouseFlags.ButtonShift;
  823. if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightAltPressed) ||
  824. mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftAltPressed))
  825. mouseFlag |= MouseFlags.ButtonAlt;
  826. return mouseFlag;
  827. }
  828. KeyModifiers keyModifiers;
  829. public ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEventRecord keyEvent)
  830. {
  831. var state = keyEvent.dwControlKeyState;
  832. bool shift = (state & WindowsConsole.ControlKeyState.ShiftPressed) != 0;
  833. bool alt = (state & (WindowsConsole.ControlKeyState.LeftAltPressed | WindowsConsole.ControlKeyState.RightAltPressed)) != 0;
  834. bool control = (state & (WindowsConsole.ControlKeyState.LeftControlPressed | WindowsConsole.ControlKeyState.RightControlPressed)) != 0;
  835. bool capslock = (state & (WindowsConsole.ControlKeyState.CapslockOn)) != 0;
  836. bool numlock = (state & (WindowsConsole.ControlKeyState.NumlockOn)) != 0;
  837. bool scrolllock = (state & (WindowsConsole.ControlKeyState.ScrolllockOn)) != 0;
  838. if (keyModifiers == null)
  839. keyModifiers = new KeyModifiers ();
  840. if (shift)
  841. keyModifiers.Shift = shift;
  842. if (alt)
  843. keyModifiers.Alt = alt;
  844. if (control)
  845. keyModifiers.Ctrl = control;
  846. if (capslock)
  847. keyModifiers.Capslock = capslock;
  848. if (numlock)
  849. keyModifiers.Numlock = numlock;
  850. if (scrolllock)
  851. keyModifiers.Scrolllock = scrolllock;
  852. var ConsoleKeyInfo = new ConsoleKeyInfo (keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control);
  853. return new ConsoleKeyInfoEx (ConsoleKeyInfo, capslock, numlock);
  854. }
  855. public Key MapKey (ConsoleKeyInfoEx keyInfoEx)
  856. {
  857. var keyInfo = keyInfoEx.consoleKeyInfo;
  858. switch (keyInfo.Key) {
  859. case ConsoleKey.Escape:
  860. return MapKeyModifiers (keyInfo, Key.Esc);
  861. case ConsoleKey.Tab:
  862. return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab;
  863. case ConsoleKey.Home:
  864. return MapKeyModifiers (keyInfo, Key.Home);
  865. case ConsoleKey.End:
  866. return MapKeyModifiers (keyInfo, Key.End);
  867. case ConsoleKey.LeftArrow:
  868. return MapKeyModifiers (keyInfo, Key.CursorLeft);
  869. case ConsoleKey.RightArrow:
  870. return MapKeyModifiers (keyInfo, Key.CursorRight);
  871. case ConsoleKey.UpArrow:
  872. return MapKeyModifiers (keyInfo, Key.CursorUp);
  873. case ConsoleKey.DownArrow:
  874. return MapKeyModifiers (keyInfo, Key.CursorDown);
  875. case ConsoleKey.PageUp:
  876. return MapKeyModifiers (keyInfo, Key.PageUp);
  877. case ConsoleKey.PageDown:
  878. return MapKeyModifiers (keyInfo, Key.PageDown);
  879. case ConsoleKey.Enter:
  880. return MapKeyModifiers (keyInfo, Key.Enter);
  881. case ConsoleKey.Spacebar:
  882. return MapKeyModifiers (keyInfo, Key.Space);
  883. case ConsoleKey.Backspace:
  884. return MapKeyModifiers (keyInfo, Key.Backspace);
  885. case ConsoleKey.Delete:
  886. return MapKeyModifiers (keyInfo, Key.DeleteChar);
  887. case ConsoleKey.Insert:
  888. return MapKeyModifiers (keyInfo, Key.InsertChar);
  889. case ConsoleKey.NumPad0:
  890. return keyInfoEx.NumLock ? (Key)(uint)'0' : Key.InsertChar;
  891. case ConsoleKey.NumPad1:
  892. return keyInfoEx.NumLock ? (Key)(uint)'1' : Key.End;
  893. case ConsoleKey.NumPad2:
  894. return keyInfoEx.NumLock ? (Key)(uint)'2' : Key.CursorDown;
  895. case ConsoleKey.NumPad3:
  896. return keyInfoEx.NumLock ? (Key)(uint)'3' : Key.PageDown;
  897. case ConsoleKey.NumPad4:
  898. return keyInfoEx.NumLock ? (Key)(uint)'4' : Key.CursorLeft;
  899. case ConsoleKey.NumPad5:
  900. return keyInfoEx.NumLock ? (Key)(uint)'5' : (Key)((uint)keyInfo.KeyChar);
  901. case ConsoleKey.NumPad6:
  902. return keyInfoEx.NumLock ? (Key)(uint)'6' : Key.CursorRight;
  903. case ConsoleKey.NumPad7:
  904. return keyInfoEx.NumLock ? (Key)(uint)'7' : Key.Home;
  905. case ConsoleKey.NumPad8:
  906. return keyInfoEx.NumLock ? (Key)(uint)'8' : Key.CursorUp;
  907. case ConsoleKey.NumPad9:
  908. return keyInfoEx.NumLock ? (Key)(uint)'9' : Key.PageUp;
  909. case ConsoleKey.Oem1:
  910. case ConsoleKey.Oem2:
  911. case ConsoleKey.Oem3:
  912. case ConsoleKey.Oem4:
  913. case ConsoleKey.Oem5:
  914. case ConsoleKey.Oem6:
  915. case ConsoleKey.Oem7:
  916. case ConsoleKey.Oem8:
  917. case ConsoleKey.Oem102:
  918. case ConsoleKey.OemPeriod:
  919. case ConsoleKey.OemComma:
  920. case ConsoleKey.OemPlus:
  921. case ConsoleKey.OemMinus:
  922. if (keyInfo.KeyChar == 0)
  923. return Key.Unknown;
  924. return (Key)((uint)keyInfo.KeyChar);
  925. }
  926. var key = keyInfo.Key;
  927. //var alphaBase = ((keyInfo.Modifiers == ConsoleModifiers.Shift) ^ (keyInfoEx.CapsLock)) ? 'A' : 'a';
  928. if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
  929. var delta = key - ConsoleKey.A;
  930. if (keyInfo.Modifiers == ConsoleModifiers.Control)
  931. return (Key)((uint)Key.ControlA + delta);
  932. if (keyInfo.Modifiers == ConsoleModifiers.Alt)
  933. return (Key)(((uint)Key.AltMask) | ((uint)'A' + delta));
  934. if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
  935. if (keyInfo.KeyChar == 0)
  936. return (Key)(((uint)Key.AltMask) + ((uint)Key.ControlA + delta));
  937. else
  938. return (Key)((uint)keyInfo.KeyChar);
  939. }
  940. //return (Key)((uint)alphaBase + delta);
  941. return (Key)((uint)keyInfo.KeyChar);
  942. }
  943. if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
  944. var delta = key - ConsoleKey.D0;
  945. if (keyInfo.Modifiers == ConsoleModifiers.Alt)
  946. return (Key)(((uint)Key.AltMask) | ((uint)'0' + delta));
  947. return (Key)((uint)keyInfo.KeyChar);
  948. }
  949. if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) {
  950. var delta = key - ConsoleKey.F1;
  951. return (Key)((int)Key.F1 + delta);
  952. }
  953. return (Key)(0xffffffff);
  954. }
  955. private Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
  956. {
  957. Key keyMod = new Key ();
  958. if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Shift))
  959. keyMod = Key.ShiftMask;
  960. if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control))
  961. keyMod |= Key.CtrlMask;
  962. if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt))
  963. keyMod |= Key.AltMask;
  964. return keyMod != Key.ControlSpace ? keyMod | key : key;
  965. }
  966. public override void Init (Action terminalResized)
  967. {
  968. TerminalResized = terminalResized;
  969. }
  970. void ResizeScreen ()
  971. {
  972. OutputBuffer = new WindowsConsole.CharInfo [Rows * Cols];
  973. Clip = new Rect (0, 0, Cols, Rows);
  974. damageRegion = new WindowsConsole.SmallRect () {
  975. Top = 0,
  976. Left = 0,
  977. Bottom = (short)Rows,
  978. Right = (short)Cols
  979. };
  980. }
  981. void UpdateOffScreen ()
  982. {
  983. for (int row = 0; row < rows; row++)
  984. for (int col = 0; col < cols; col++) {
  985. int position = row * cols + col;
  986. OutputBuffer [position].Attributes = (ushort)Colors.TopLevel.Normal;
  987. OutputBuffer [position].Char.UnicodeChar = ' ';
  988. }
  989. }
  990. int ccol, crow;
  991. public override void Move (int col, int row)
  992. {
  993. ccol = col;
  994. crow = row;
  995. }
  996. public override void AddRune (Rune rune)
  997. {
  998. var position = crow * Cols + ccol;
  999. if (Clip.Contains (ccol, crow)) {
  1000. OutputBuffer [position].Attributes = (ushort)currentAttribute;
  1001. OutputBuffer [position].Char.UnicodeChar = (char)rune;
  1002. WindowsConsole.SmallRect.Update (ref damageRegion, (short)ccol, (short)crow);
  1003. }
  1004. ccol++;
  1005. var runeWidth = Rune.ColumnWidth (rune);
  1006. if (runeWidth > 1) {
  1007. for (int i = 1; i < runeWidth; i++) {
  1008. AddStr (" ");
  1009. }
  1010. }
  1011. //if (ccol == Cols) {
  1012. // ccol = 0;
  1013. // if (crow + 1 < Rows)
  1014. // crow++;
  1015. //}
  1016. if (sync)
  1017. UpdateScreen ();
  1018. }
  1019. public override void AddStr (ustring str)
  1020. {
  1021. foreach (var rune in str)
  1022. AddRune (rune);
  1023. }
  1024. int currentAttribute;
  1025. CancellationTokenSource tokenSource = new CancellationTokenSource ();
  1026. public override void SetAttribute (Attribute c)
  1027. {
  1028. currentAttribute = c.value;
  1029. }
  1030. public override Attribute MakeAttribute (Color fore, Color back)
  1031. {
  1032. return MakeColor ((ConsoleColor)fore, (ConsoleColor)back);
  1033. }
  1034. public override void Refresh ()
  1035. {
  1036. UpdateScreen ();
  1037. #if false
  1038. var bufferCoords = new WindowsConsole.Coord (){
  1039. X = (short)Clip.Width,
  1040. Y = (short)Clip.Height
  1041. };
  1042. var window = new WindowsConsole.SmallRect (){
  1043. Top = 0,
  1044. Left = 0,
  1045. Right = (short)Clip.Right,
  1046. Bottom = (short)Clip.Bottom
  1047. };
  1048. UpdateCursor();
  1049. winConsole.WriteToConsole (OutputBuffer, bufferCoords, window);
  1050. #endif
  1051. }
  1052. public override void UpdateScreen ()
  1053. {
  1054. if (damageRegion.Left == -1)
  1055. return;
  1056. var bufferCoords = new WindowsConsole.Coord () {
  1057. X = (short)Clip.Width,
  1058. Y = (short)Clip.Height
  1059. };
  1060. var window = new WindowsConsole.SmallRect () {
  1061. Top = 0,
  1062. Left = 0,
  1063. Right = (short)Clip.Right,
  1064. Bottom = (short)Clip.Bottom
  1065. };
  1066. UpdateCursor ();
  1067. winConsole.WriteToConsole (OutputBuffer, bufferCoords, damageRegion);
  1068. // System.Diagnostics.Debugger.Log(0, "debug", $"Region={damageRegion.Right - damageRegion.Left},{damageRegion.Bottom - damageRegion.Top}\n");
  1069. WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
  1070. }
  1071. public override void UpdateCursor ()
  1072. {
  1073. var position = new WindowsConsole.Coord () {
  1074. X = (short)ccol,
  1075. Y = (short)crow
  1076. };
  1077. winConsole.SetCursorPosition (position);
  1078. }
  1079. public override void End ()
  1080. {
  1081. winConsole.Cleanup ();
  1082. }
  1083. #region Unused
  1084. public override void SetColors (ConsoleColor foreground, ConsoleColor background)
  1085. {
  1086. }
  1087. public override void SetColors (short foregroundColorId, short backgroundColorId)
  1088. {
  1089. }
  1090. public override void Suspend ()
  1091. {
  1092. }
  1093. public override void StartReportingMouseMoves ()
  1094. {
  1095. }
  1096. public override void StopReportingMouseMoves ()
  1097. {
  1098. }
  1099. public override void UncookMouse ()
  1100. {
  1101. }
  1102. public override void CookMouse ()
  1103. {
  1104. }
  1105. public override ConsoleFont GetFont ()
  1106. {
  1107. return winConsole.GetFont ();
  1108. }
  1109. public override Capabilites GetCapabilites ()
  1110. {
  1111. throw new NotImplementedException ();
  1112. }
  1113. #endregion
  1114. }
  1115. }