WindowsDriver.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790
  1. //
  2. // WindowsDriver.cs: Windows specific driver
  3. //
  4. // Authors:
  5. // Nick Van Dyck ([email protected])
  6. //
  7. // Copyright (c) 2018
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining a copy
  10. // of this software and associated documentation files (the "Software"), to deal
  11. // in the Software without restriction, including without limitation the rights
  12. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. // copies of the Software, and to permit persons to whom the Software is
  14. // furnished to do so, subject to the following conditions:
  15. //
  16. // The above copyright notice and this permission notice shall be included in all
  17. // copies or substantial portions of the Software.
  18. //
  19. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  25. // SOFTWARE.
  26. //
  27. using System;
  28. using System.Runtime.InteropServices;
  29. using System.Threading.Tasks;
  30. using Mono.Terminal;
  31. using NStack;
  32. namespace Terminal.Gui {
  33. internal class WindowsConsole {
  34. public const int STD_OUTPUT_HANDLE = -11;
  35. public const int STD_INPUT_HANDLE = -10;
  36. public const int STD_ERROR_HANDLE = -12;
  37. public IntPtr InputHandle, OutputHandle;
  38. public IntPtr ScreenBuffer;
  39. public WindowsConsole ()
  40. {
  41. InputHandle = GetStdHandle (STD_INPUT_HANDLE);
  42. OutputHandle = GetStdHandle (STD_OUTPUT_HANDLE);
  43. }
  44. public CharInfo[] OriginalStdOutChars;
  45. public bool WriteToConsole (CharInfo[] charInfoBuffer, Coord coords, SmallRect window)
  46. {
  47. if (ScreenBuffer == IntPtr.Zero)
  48. {
  49. ScreenBuffer = CreateConsoleScreenBuffer (
  50. DesiredAccess.GenericRead | DesiredAccess.GenericWrite,
  51. ShareMode.FileShareRead | ShareMode.FileShareWrite,
  52. IntPtr.Zero,
  53. 1,
  54. IntPtr.Zero
  55. );
  56. var err = Marshal.GetLastWin32Error ();
  57. if (err != 0)
  58. {
  59. throw new System.ComponentModel.Win32Exception(err);
  60. }
  61. if (!SetConsoleActiveScreenBuffer (ScreenBuffer))
  62. {
  63. err = Marshal.GetLastWin32Error();
  64. throw new System.ComponentModel.Win32Exception(err);
  65. }
  66. OriginalStdOutChars = new CharInfo[Console.WindowHeight * Console.WindowWidth];
  67. ReadConsoleOutput (
  68. OutputHandle,
  69. OriginalStdOutChars,
  70. coords,
  71. new Coord() { X = 0, Y = 0 },
  72. ref window
  73. );
  74. }
  75. return WriteConsoleOutput (
  76. ScreenBuffer,
  77. charInfoBuffer,
  78. coords,
  79. new Coord() { X = 0, Y = 0 },
  80. ref window
  81. );
  82. }
  83. public bool SetCursorPosition(Coord position)
  84. {
  85. return SetConsoleCursorPosition (ScreenBuffer, position);
  86. }
  87. public void PollEvents (Action<InputRecord> inputEventHandler)
  88. {
  89. if (OriginalConsoleMode != 0)
  90. return;
  91. OriginalConsoleMode = ConsoleMode;
  92. ConsoleMode |= (uint)ConsoleModes.EnableMouseInput;
  93. ConsoleMode &= ~(uint)ConsoleModes.EnableQuickEditMode;
  94. ConsoleMode |= (uint)ConsoleModes.EnableExtendedFlags;
  95. Task.Run (() =>
  96. {
  97. uint numberEventsRead = 0;
  98. uint length = 1;
  99. InputRecord[] records = new InputRecord[length];
  100. while (
  101. ContinueListeningForConsoleEvents &&
  102. ReadConsoleInput(InputHandle, records, length, out numberEventsRead) &&
  103. numberEventsRead > 0
  104. )
  105. {
  106. inputEventHandler (records[0]);
  107. }
  108. });
  109. }
  110. public void Cleanup ()
  111. {
  112. ContinueListeningForConsoleEvents = false;
  113. ConsoleMode = OriginalConsoleMode;
  114. OriginalConsoleMode = 0;
  115. if (!SetConsoleActiveScreenBuffer (OutputHandle))
  116. {
  117. var err = Marshal.GetLastWin32Error ();
  118. Console.WriteLine("Error: {0}", err);
  119. }
  120. }
  121. private bool ContinueListeningForConsoleEvents = true;
  122. private uint OriginalConsoleMode = 0;
  123. public uint ConsoleMode {
  124. get {
  125. uint v;
  126. GetConsoleMode (InputHandle, out v);
  127. return v;
  128. }
  129. set {
  130. SetConsoleMode (InputHandle, value);
  131. }
  132. }
  133. [Flags]
  134. public enum ConsoleModes : uint
  135. {
  136. EnableMouseInput = 16,
  137. EnableQuickEditMode = 64,
  138. EnableExtendedFlags = 128,
  139. }
  140. [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)]
  141. public struct KeyEventRecord {
  142. [FieldOffset (0), MarshalAs (UnmanagedType.Bool)]
  143. public bool bKeyDown;
  144. [FieldOffset (4), MarshalAs (UnmanagedType.U2)]
  145. public ushort wRepeatCount;
  146. [FieldOffset (6), MarshalAs (UnmanagedType.U2)]
  147. public ushort wVirtualKeyCode;
  148. [FieldOffset (8), MarshalAs (UnmanagedType.U2)]
  149. public ushort wVirtualScanCode;
  150. [FieldOffset (10)]
  151. public char UnicodeChar;
  152. [FieldOffset (12), MarshalAs (UnmanagedType.U4)]
  153. public ControlKeyState dwControlKeyState;
  154. }
  155. [Flags]
  156. public enum ButtonState {
  157. Button1Pressed = 1,
  158. Button2Pressed = 4,
  159. Button3Pressed = 8,
  160. Button4Pressed = 16,
  161. RightmostButtonPressed = 2,
  162. }
  163. [Flags]
  164. public enum ControlKeyState {
  165. RightAltPressed = 1,
  166. LeftAltPressed = 2,
  167. RightControlPressed = 4,
  168. LeftControlPressed = 8,
  169. ShiftPressed = 16,
  170. NumlockOn = 32,
  171. ScrolllockOn = 64,
  172. CapslockOn = 128,
  173. EnhancedKey = 256
  174. }
  175. [Flags]
  176. public enum EventFlags {
  177. MouseMoved = 1,
  178. DoubleClick = 2,
  179. MouseWheeled = 4,
  180. MouseHorizontalWheeled = 8
  181. }
  182. [StructLayout (LayoutKind.Explicit)]
  183. public struct MouseEventRecord {
  184. [FieldOffset (0)]
  185. public Coordinate MousePosition;
  186. [FieldOffset (4)]
  187. public ButtonState ButtonState;
  188. [FieldOffset (8)]
  189. public ControlKeyState ControlKeyState;
  190. [FieldOffset (12)]
  191. public EventFlags EventFlags;
  192. public override string ToString ()
  193. {
  194. return $"[Mouse({MousePosition},{ButtonState},{ControlKeyState},{EventFlags}";
  195. }
  196. }
  197. [StructLayout (LayoutKind.Sequential)]
  198. public struct Coordinate {
  199. public short X;
  200. public short Y;
  201. public Coordinate (short X, short Y)
  202. {
  203. this.X = X;
  204. this.Y = Y;
  205. }
  206. public override string ToString () => $"({X},{Y})";
  207. };
  208. internal struct WindowBufferSizeRecord {
  209. public Coordinate size;
  210. public WindowBufferSizeRecord (short x, short y)
  211. {
  212. this.size = new Coordinate (x, y);
  213. }
  214. public override string ToString () => $"[WindowBufferSize{size}";
  215. }
  216. [StructLayout (LayoutKind.Sequential)]
  217. public struct MenuEventRecord {
  218. public uint dwCommandId;
  219. }
  220. [StructLayout (LayoutKind.Sequential)]
  221. public struct FocusEventRecord {
  222. public uint bSetFocus;
  223. }
  224. public enum EventType {
  225. Focus = 0x10,
  226. Key = 0x1,
  227. Menu = 0x8,
  228. Mouse = 2,
  229. WindowBufferSize = 4
  230. }
  231. [StructLayout (LayoutKind.Explicit)]
  232. public struct InputRecord {
  233. [FieldOffset (0)]
  234. public EventType EventType;
  235. [FieldOffset (4)]
  236. public KeyEventRecord KeyEvent;
  237. [FieldOffset (4)]
  238. public MouseEventRecord MouseEvent;
  239. [FieldOffset (4)]
  240. public WindowBufferSizeRecord WindowBufferSizeEvent;
  241. [FieldOffset (4)]
  242. public MenuEventRecord MenuEvent;
  243. [FieldOffset (4)]
  244. public FocusEventRecord FocusEvent;
  245. public override string ToString ()
  246. {
  247. switch (EventType) {
  248. case EventType.Focus:
  249. return FocusEvent.ToString ();
  250. case EventType.Key:
  251. return KeyEvent.ToString ();
  252. case EventType.Menu:
  253. return MenuEvent.ToString ();
  254. case EventType.Mouse:
  255. return MouseEvent.ToString ();
  256. case EventType.WindowBufferSize:
  257. return WindowBufferSizeEvent.ToString ();
  258. default:
  259. return "Unknown event type: " + EventType;
  260. }
  261. }
  262. };
  263. [Flags]
  264. enum ShareMode : uint
  265. {
  266. FileShareRead = 1,
  267. FileShareWrite = 2,
  268. }
  269. [Flags]
  270. enum DesiredAccess : uint
  271. {
  272. GenericRead = 2147483648,
  273. GenericWrite = 1073741824,
  274. }
  275. [StructLayout(LayoutKind.Sequential)]
  276. public struct ConsoleScreenBufferInfo
  277. {
  278. public Coord dwSize;
  279. public Coord dwCursorPosition;
  280. public ushort wAttributes;
  281. public SmallRect srWindow;
  282. public Coord dwMaximumWindowSize;
  283. }
  284. [StructLayout(LayoutKind.Sequential)]
  285. public struct Coord
  286. {
  287. public short X;
  288. public short Y;
  289. public Coord(short X, short Y)
  290. {
  291. this.X = X;
  292. this.Y = Y;
  293. }
  294. };
  295. [StructLayout(LayoutKind.Explicit, CharSet=CharSet.Unicode)]
  296. public struct CharUnion
  297. {
  298. [FieldOffset(0)] public char UnicodeChar;
  299. [FieldOffset(0)] public byte AsciiChar;
  300. }
  301. [StructLayout(LayoutKind.Explicit, CharSet=CharSet.Unicode)]
  302. public struct CharInfo
  303. {
  304. [FieldOffset(0)] public CharUnion Char;
  305. [FieldOffset(2)] public ushort Attributes;
  306. }
  307. [StructLayout(LayoutKind.Sequential)]
  308. public struct SmallRect
  309. {
  310. public short Left;
  311. public short Top;
  312. public short Right;
  313. public short Bottom;
  314. }
  315. [DllImport ("kernel32.dll", SetLastError = true)]
  316. static extern IntPtr GetStdHandle (int nStdHandle);
  317. [DllImport ("kernel32.dll", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode)]
  318. public static extern bool ReadConsoleInput (
  319. IntPtr hConsoleInput,
  320. [Out] InputRecord [] lpBuffer,
  321. uint nLength,
  322. out uint lpNumberOfEventsRead);
  323. [DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
  324. static extern bool ReadConsoleOutput(
  325. IntPtr hConsoleOutput,
  326. [Out] CharInfo[] lpBuffer,
  327. Coord dwBufferSize,
  328. Coord dwBufferCoord,
  329. ref SmallRect lpReadRegion
  330. );
  331. [DllImport("kernel32.dll", EntryPoint="WriteConsoleOutput", SetLastError=true, CharSet=CharSet.Unicode)]
  332. static extern bool WriteConsoleOutput(
  333. IntPtr hConsoleOutput,
  334. CharInfo[] lpBuffer,
  335. Coord dwBufferSize,
  336. Coord dwBufferCoord,
  337. ref SmallRect lpWriteRegion
  338. );
  339. [DllImport ("kernel32.dll")]
  340. static extern bool SetConsoleCursorPosition(IntPtr hConsoleOutput, Coord dwCursorPosition);
  341. [DllImport ("kernel32.dll")]
  342. static extern bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode);
  343. [DllImport ("kernel32.dll")]
  344. static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode);
  345. [DllImport("kernel32.dll", SetLastError = true)]
  346. static extern IntPtr CreateConsoleScreenBuffer(
  347. DesiredAccess dwDesiredAccess,
  348. ShareMode dwShareMode,
  349. IntPtr secutiryAttributes,
  350. UInt32 flags,
  351. IntPtr screenBufferData
  352. );
  353. [DllImport("kernel32.dll", SetLastError = true)]
  354. static extern bool SetConsoleActiveScreenBuffer(IntPtr Handle);
  355. }
  356. internal class WindowsDriver : ConsoleDriver {
  357. Action TerminalResized;
  358. WindowsConsole WinConsole;
  359. WindowsConsole.CharInfo[] OutputBuffer;
  360. int cols, rows;
  361. public override int Cols => cols;
  362. public override int Rows => rows;
  363. static bool sync;
  364. public WindowsDriver ()
  365. {
  366. WinConsole = new WindowsConsole();
  367. cols = Console.WindowWidth;
  368. rows = Console.WindowHeight - 1;
  369. ResizeScreen ();
  370. UpdateOffScreen ();
  371. }
  372. private MouseEvent ToDriverMouse(WindowsConsole.MouseEventRecord mouseEvent)
  373. {
  374. MouseFlags mouseFlag = MouseFlags.AllEvents;
  375. if (mouseEvent.EventFlags == 0)
  376. {
  377. switch (mouseEvent.ButtonState)
  378. {
  379. case WindowsConsole.ButtonState.Button1Pressed:
  380. mouseFlag = MouseFlags.Button1Clicked;
  381. break;
  382. case WindowsConsole.ButtonState.Button2Pressed:
  383. mouseFlag = MouseFlags.Button2Clicked;
  384. break;
  385. case WindowsConsole.ButtonState.Button3Pressed:
  386. mouseFlag = MouseFlags.Button3Clicked;
  387. break;
  388. }
  389. }
  390. else if(mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved)
  391. {
  392. mouseFlag = MouseFlags.ReportMousePosition;
  393. }
  394. return new MouseEvent () {
  395. X = mouseEvent.MousePosition.X,
  396. Y = mouseEvent.MousePosition.Y,
  397. Flags = mouseFlag
  398. };
  399. }
  400. private ConsoleKeyInfo ToConsoleKeyInfo (WindowsConsole.KeyEventRecord keyEvent)
  401. {
  402. var state = keyEvent.dwControlKeyState;
  403. bool shift = (state & WindowsConsole.ControlKeyState.ShiftPressed) != 0;
  404. bool alt = (state & (WindowsConsole.ControlKeyState.LeftAltPressed | WindowsConsole.ControlKeyState.RightAltPressed)) != 0;
  405. bool control = (state & (WindowsConsole.ControlKeyState.LeftControlPressed | WindowsConsole.ControlKeyState.RightControlPressed)) != 0;
  406. return new ConsoleKeyInfo(keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control);
  407. }
  408. public Key MapKey (ConsoleKeyInfo keyInfo)
  409. {
  410. switch (keyInfo.Key) {
  411. case ConsoleKey.Escape:
  412. return Key.Esc;
  413. case ConsoleKey.Tab:
  414. return Key.Tab;
  415. case ConsoleKey.Home:
  416. return Key.Home;
  417. case ConsoleKey.End:
  418. return Key.End;
  419. case ConsoleKey.LeftArrow:
  420. return Key.CursorLeft;
  421. case ConsoleKey.RightArrow:
  422. return Key.CursorRight;
  423. case ConsoleKey.UpArrow:
  424. return Key.CursorUp;
  425. case ConsoleKey.DownArrow:
  426. return Key.CursorDown;
  427. case ConsoleKey.PageUp:
  428. return Key.PageUp;
  429. case ConsoleKey.PageDown:
  430. return Key.PageDown;
  431. case ConsoleKey.Enter:
  432. return Key.Enter;
  433. case ConsoleKey.Spacebar:
  434. return Key.Space;
  435. case ConsoleKey.Backspace:
  436. return Key.Backspace;
  437. case ConsoleKey.Delete:
  438. return Key.Delete;
  439. case ConsoleKey.Oem1:
  440. case ConsoleKey.Oem2:
  441. case ConsoleKey.Oem3:
  442. case ConsoleKey.Oem4:
  443. case ConsoleKey.Oem5:
  444. case ConsoleKey.Oem6:
  445. case ConsoleKey.Oem7:
  446. case ConsoleKey.Oem8:
  447. case ConsoleKey.Oem102:
  448. case ConsoleKey.OemPeriod:
  449. case ConsoleKey.OemComma:
  450. case ConsoleKey.OemPlus:
  451. case ConsoleKey.OemMinus:
  452. return (Key)((uint)keyInfo.KeyChar);
  453. }
  454. var key = keyInfo.Key;
  455. if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
  456. var delta = key - ConsoleKey.A;
  457. if (keyInfo.Modifiers == ConsoleModifiers.Control)
  458. return (Key)((uint)Key.ControlA + delta);
  459. if (keyInfo.Modifiers == ConsoleModifiers.Alt)
  460. return (Key)(((uint)Key.AltMask) | ((uint)'A' + delta));
  461. if (keyInfo.Modifiers == ConsoleModifiers.Shift)
  462. return (Key)((uint)'A' + delta);
  463. else
  464. return (Key)((uint)'a' + delta);
  465. }
  466. if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
  467. var delta = key - ConsoleKey.D0;
  468. if (keyInfo.Modifiers == ConsoleModifiers.Alt)
  469. return (Key)(((uint)Key.AltMask) | ((uint)'0' + delta));
  470. if (keyInfo.Modifiers == ConsoleModifiers.Shift)
  471. return (Key)((uint)keyInfo.KeyChar);
  472. return (Key)((uint)'0' + delta);
  473. }
  474. if (key >= ConsoleKey.F1 && key <= ConsoleKey.F10) {
  475. var delta = key - ConsoleKey.F1;
  476. return (Key)((int)Key.F1 + delta);
  477. }
  478. return (Key)(0xffffffff);
  479. }
  480. public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<MouseEvent> mouseHandler)
  481. {
  482. WinConsole.PollEvents (inputEvent =>
  483. {
  484. switch(inputEvent.EventType)
  485. {
  486. case WindowsConsole.EventType.Key:
  487. if (inputEvent.KeyEvent.bKeyDown == false)
  488. return;
  489. var map = MapKey (ToConsoleKeyInfo (inputEvent.KeyEvent));
  490. if (map == (Key) 0xffffffff)
  491. return;
  492. keyHandler (new KeyEvent (map));
  493. break;
  494. case WindowsConsole.EventType.Mouse:
  495. mouseHandler (ToDriverMouse (inputEvent.MouseEvent));
  496. break;
  497. case WindowsConsole.EventType.WindowBufferSize:
  498. cols = inputEvent.WindowBufferSizeEvent.size.X;
  499. rows = inputEvent.WindowBufferSizeEvent.size.Y - 1;
  500. ResizeScreen ();
  501. UpdateOffScreen ();
  502. TerminalResized ();
  503. break;
  504. }
  505. });
  506. }
  507. public override void Init (Action terminalResized)
  508. {
  509. TerminalResized = terminalResized;
  510. Colors.Base = new ColorScheme ();
  511. Colors.Dialog = new ColorScheme ();
  512. Colors.Menu = new ColorScheme ();
  513. Colors.Error = new ColorScheme ();
  514. HLine = '\u2500';
  515. VLine = '\u2502';
  516. Stipple = '\u2592';
  517. Diamond = '\u25c6';
  518. ULCorner = '\u250C';
  519. LLCorner = '\u2514';
  520. URCorner = '\u2510';
  521. LRCorner = '\u2518';
  522. LeftTee = '\u251c';
  523. RightTee = '\u2524';
  524. TopTee = '\u22a4';
  525. BottomTee = '\u22a5';
  526. Colors.Base.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Blue);
  527. Colors.Base.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Cyan);
  528. Colors.Base.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Blue);
  529. Colors.Base.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan);
  530. Colors.Menu.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Black);
  531. Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
  532. Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan);
  533. Colors.Menu.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Cyan);
  534. Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
  535. Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Cyan);
  536. Colors.Dialog.HotNormal = MakeColor (ConsoleColor.Blue, ConsoleColor.Gray);
  537. Colors.Dialog.HotFocus = MakeColor (ConsoleColor.Blue, ConsoleColor.Cyan);
  538. Colors.Error.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Red);
  539. Colors.Error.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
  540. Colors.Error.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Red);
  541. Colors.Error.HotFocus = Colors.Error.HotNormal;
  542. Console.Clear ();
  543. }
  544. void ResizeScreen ()
  545. {
  546. OutputBuffer = new WindowsConsole.CharInfo[Rows * Cols];
  547. Clip = new Rect (0, 0, Cols, Rows);
  548. }
  549. void UpdateOffScreen ()
  550. {
  551. for (int row = 0; row < rows; row++)
  552. for (int col = 0; col < cols; col++)
  553. {
  554. int position = row * cols + col;
  555. OutputBuffer[position].Attributes = (ushort)MakeColor(ConsoleColor.White, ConsoleColor.Blue);
  556. OutputBuffer[position].Char.UnicodeChar = ' ';
  557. }
  558. }
  559. int ccol, crow;
  560. public override void Move (int col, int row)
  561. {
  562. ccol = col;
  563. crow = row;
  564. }
  565. public override void AddRune (Rune rune)
  566. {
  567. var position = crow * Cols + ccol;
  568. if (Clip.Contains (ccol, crow))
  569. {
  570. OutputBuffer[position].Attributes = (ushort)currentAttribute;
  571. OutputBuffer[position].Char.UnicodeChar = (char)rune;
  572. }
  573. ccol++;
  574. if (ccol == Cols) {
  575. ccol = 0;
  576. if (crow + 1 < Rows)
  577. crow++;
  578. }
  579. if (sync)
  580. UpdateScreen ();
  581. }
  582. public override void AddStr (ustring str)
  583. {
  584. foreach (var rune in str)
  585. AddRune (rune);
  586. }
  587. int currentAttribute;
  588. public override void SetAttribute (Attribute c)
  589. {
  590. currentAttribute = c.value;
  591. }
  592. private Attribute MakeColor (ConsoleColor f, ConsoleColor b)
  593. {
  594. // Encode the colors into the int value.
  595. return new Attribute ()
  596. {
  597. value = ((int)f | (int)b << 4)
  598. };
  599. }
  600. public override void Refresh()
  601. {
  602. var bufferCoords = new WindowsConsole.Coord ()
  603. {
  604. X = (short)Clip.Width,
  605. Y = (short)Clip.Height
  606. };
  607. var window = new WindowsConsole.SmallRect ()
  608. {
  609. Top = 0,
  610. Left = 0,
  611. Right = (short)Clip.Right,
  612. Bottom = (short)Clip.Bottom
  613. };
  614. UpdateCursor();
  615. WinConsole.WriteToConsole (OutputBuffer, bufferCoords, window);
  616. }
  617. public override void UpdateScreen ()
  618. {
  619. var bufferCoords = new WindowsConsole.Coord ()
  620. {
  621. X = (short)Clip.Width,
  622. Y = (short)Clip.Height
  623. };
  624. var window = new WindowsConsole.SmallRect ()
  625. {
  626. Top = 0,
  627. Left = 0,
  628. Right = (short)Clip.Right,
  629. Bottom = (short)Clip.Bottom
  630. };
  631. UpdateCursor();
  632. WinConsole.WriteToConsole (OutputBuffer, bufferCoords, window);
  633. }
  634. public override void UpdateCursor()
  635. {
  636. var position = new WindowsConsole.Coord()
  637. {
  638. X = (short)ccol,
  639. Y = (short)crow
  640. };
  641. WinConsole.SetCursorPosition(position);
  642. }
  643. public override void End ()
  644. {
  645. WinConsole.Cleanup();
  646. }
  647. #region Unused
  648. public override void SetColors (ConsoleColor foreground, ConsoleColor background)
  649. {
  650. }
  651. public override void SetColors (short foregroundColorId, short backgroundColorId)
  652. {
  653. }
  654. public override void Suspend ()
  655. {
  656. }
  657. public override void StartReportingMouseMoves ()
  658. {
  659. }
  660. public override void StopReportingMouseMoves ()
  661. {
  662. }
  663. public override void UncookMouse ()
  664. {
  665. }
  666. public override void CookMouse ()
  667. {
  668. }
  669. #endregion
  670. }
  671. }