WindowsDriver.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820
  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. IntPtr InputHandle, OutputHandle;
  38. 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 WindowsConsole.ButtonState? LastMouseButtonPressed = null;
  373. private MouseEvent ToDriverMouse(WindowsConsole.MouseEventRecord mouseEvent)
  374. {
  375. MouseFlags mouseFlag = MouseFlags.AllEvents;
  376. // The ButtonState member of the MouseEvent structure has bit corresponding to each mouse button.
  377. // This will tell when a mouse button is pressed. When the button is released this event will
  378. // be fired with it's bit set to 0. So when the button is up ButtonState will be 0.
  379. // To map to the correct driver events we save the last pressed mouse button so we can
  380. // map to the correct clicked event.
  381. if (LastMouseButtonPressed != null && mouseEvent.ButtonState != 0)
  382. {
  383. LastMouseButtonPressed = null;
  384. }
  385. if (mouseEvent.EventFlags == 0 && LastMouseButtonPressed == null)
  386. {
  387. switch (mouseEvent.ButtonState)
  388. {
  389. case WindowsConsole.ButtonState.Button1Pressed:
  390. mouseFlag = MouseFlags.Button1Pressed;
  391. break;
  392. case WindowsConsole.ButtonState.Button2Pressed:
  393. mouseFlag = MouseFlags.Button2Pressed;
  394. break;
  395. case WindowsConsole.ButtonState.Button3Pressed:
  396. mouseFlag = MouseFlags.Button3Pressed;
  397. break;
  398. }
  399. LastMouseButtonPressed = mouseEvent.ButtonState;
  400. }
  401. else if (mouseEvent.EventFlags == 0 && LastMouseButtonPressed != null)
  402. {
  403. switch (LastMouseButtonPressed)
  404. {
  405. case WindowsConsole.ButtonState.Button1Pressed:
  406. mouseFlag = MouseFlags.Button1Clicked;
  407. break;
  408. case WindowsConsole.ButtonState.Button2Pressed:
  409. mouseFlag = MouseFlags.Button2Clicked;
  410. break;
  411. case WindowsConsole.ButtonState.Button3Pressed:
  412. mouseFlag = MouseFlags.Button3Clicked;
  413. break;
  414. }
  415. LastMouseButtonPressed = null;
  416. }
  417. else if(mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved)
  418. {
  419. mouseFlag = MouseFlags.ReportMousePosition;
  420. }
  421. return new MouseEvent () {
  422. X = mouseEvent.MousePosition.X,
  423. Y = mouseEvent.MousePosition.Y,
  424. Flags = mouseFlag
  425. };
  426. }
  427. private ConsoleKeyInfo ToConsoleKeyInfo (WindowsConsole.KeyEventRecord keyEvent)
  428. {
  429. var state = keyEvent.dwControlKeyState;
  430. bool shift = (state & WindowsConsole.ControlKeyState.ShiftPressed) != 0;
  431. bool alt = (state & (WindowsConsole.ControlKeyState.LeftAltPressed | WindowsConsole.ControlKeyState.RightAltPressed)) != 0;
  432. bool control = (state & (WindowsConsole.ControlKeyState.LeftControlPressed | WindowsConsole.ControlKeyState.RightControlPressed)) != 0;
  433. return new ConsoleKeyInfo(keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control);
  434. }
  435. public Key MapKey (ConsoleKeyInfo keyInfo)
  436. {
  437. switch (keyInfo.Key) {
  438. case ConsoleKey.Escape:
  439. return Key.Esc;
  440. case ConsoleKey.Tab:
  441. return Key.Tab;
  442. case ConsoleKey.Home:
  443. return Key.Home;
  444. case ConsoleKey.End:
  445. return Key.End;
  446. case ConsoleKey.LeftArrow:
  447. return Key.CursorLeft;
  448. case ConsoleKey.RightArrow:
  449. return Key.CursorRight;
  450. case ConsoleKey.UpArrow:
  451. return Key.CursorUp;
  452. case ConsoleKey.DownArrow:
  453. return Key.CursorDown;
  454. case ConsoleKey.PageUp:
  455. return Key.PageUp;
  456. case ConsoleKey.PageDown:
  457. return Key.PageDown;
  458. case ConsoleKey.Enter:
  459. return Key.Enter;
  460. case ConsoleKey.Spacebar:
  461. return Key.Space;
  462. case ConsoleKey.Backspace:
  463. return Key.Backspace;
  464. case ConsoleKey.Delete:
  465. return Key.DeleteChar;
  466. case ConsoleKey.Oem1:
  467. case ConsoleKey.Oem2:
  468. case ConsoleKey.Oem3:
  469. case ConsoleKey.Oem4:
  470. case ConsoleKey.Oem5:
  471. case ConsoleKey.Oem6:
  472. case ConsoleKey.Oem7:
  473. case ConsoleKey.Oem8:
  474. case ConsoleKey.Oem102:
  475. case ConsoleKey.OemPeriod:
  476. case ConsoleKey.OemComma:
  477. case ConsoleKey.OemPlus:
  478. case ConsoleKey.OemMinus:
  479. return (Key)((uint)keyInfo.KeyChar);
  480. }
  481. var key = keyInfo.Key;
  482. if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
  483. var delta = key - ConsoleKey.A;
  484. if (keyInfo.Modifiers == ConsoleModifiers.Control)
  485. return (Key)((uint)Key.ControlA + delta);
  486. if (keyInfo.Modifiers == ConsoleModifiers.Alt)
  487. return (Key)(((uint)Key.AltMask) | ((uint)'A' + delta));
  488. if (keyInfo.Modifiers == ConsoleModifiers.Shift)
  489. return (Key)((uint)'A' + delta);
  490. else
  491. return (Key)((uint)'a' + delta);
  492. }
  493. if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
  494. var delta = key - ConsoleKey.D0;
  495. if (keyInfo.Modifiers == ConsoleModifiers.Alt)
  496. return (Key)(((uint)Key.AltMask) | ((uint)'0' + delta));
  497. if (keyInfo.Modifiers == ConsoleModifiers.Shift)
  498. return (Key)((uint)keyInfo.KeyChar);
  499. return (Key)((uint)'0' + delta);
  500. }
  501. if (key >= ConsoleKey.F1 && key <= ConsoleKey.F10) {
  502. var delta = key - ConsoleKey.F1;
  503. return (Key)((int)Key.F1 + delta);
  504. }
  505. return (Key)(0xffffffff);
  506. }
  507. public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<MouseEvent> mouseHandler)
  508. {
  509. WinConsole.PollEvents (inputEvent =>
  510. {
  511. switch(inputEvent.EventType)
  512. {
  513. case WindowsConsole.EventType.Key:
  514. if (inputEvent.KeyEvent.bKeyDown == false)
  515. return;
  516. var map = MapKey (ToConsoleKeyInfo (inputEvent.KeyEvent));
  517. if (map == (Key) 0xffffffff)
  518. return;
  519. keyHandler (new KeyEvent (map));
  520. break;
  521. case WindowsConsole.EventType.Mouse:
  522. mouseHandler (ToDriverMouse (inputEvent.MouseEvent));
  523. break;
  524. case WindowsConsole.EventType.WindowBufferSize:
  525. cols = inputEvent.WindowBufferSizeEvent.size.X;
  526. rows = inputEvent.WindowBufferSizeEvent.size.Y - 1;
  527. ResizeScreen ();
  528. UpdateOffScreen ();
  529. TerminalResized ();
  530. break;
  531. }
  532. });
  533. }
  534. public override void Init (Action terminalResized)
  535. {
  536. TerminalResized = terminalResized;
  537. Colors.Base = new ColorScheme ();
  538. Colors.Dialog = new ColorScheme ();
  539. Colors.Menu = new ColorScheme ();
  540. Colors.Error = new ColorScheme ();
  541. HLine = '\u2500';
  542. VLine = '\u2502';
  543. Stipple = '\u2592';
  544. Diamond = '\u25c6';
  545. ULCorner = '\u250C';
  546. LLCorner = '\u2514';
  547. URCorner = '\u2510';
  548. LRCorner = '\u2518';
  549. LeftTee = '\u251c';
  550. RightTee = '\u2524';
  551. TopTee = '\u22a4';
  552. BottomTee = '\u22a5';
  553. Colors.Base.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Blue);
  554. Colors.Base.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Cyan);
  555. Colors.Base.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Blue);
  556. Colors.Base.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan);
  557. Colors.Menu.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Black);
  558. Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
  559. Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan);
  560. Colors.Menu.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Cyan);
  561. Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
  562. Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Cyan);
  563. Colors.Dialog.HotNormal = MakeColor (ConsoleColor.Blue, ConsoleColor.Gray);
  564. Colors.Dialog.HotFocus = MakeColor (ConsoleColor.Blue, ConsoleColor.Cyan);
  565. Colors.Error.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Red);
  566. Colors.Error.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
  567. Colors.Error.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Red);
  568. Colors.Error.HotFocus = Colors.Error.HotNormal;
  569. Console.Clear ();
  570. }
  571. void ResizeScreen ()
  572. {
  573. OutputBuffer = new WindowsConsole.CharInfo[Rows * Cols];
  574. Clip = new Rect (0, 0, Cols, Rows);
  575. }
  576. void UpdateOffScreen ()
  577. {
  578. for (int row = 0; row < rows; row++)
  579. for (int col = 0; col < cols; col++)
  580. {
  581. int position = row * cols + col;
  582. OutputBuffer[position].Attributes = (ushort)MakeColor(ConsoleColor.White, ConsoleColor.Blue);
  583. OutputBuffer[position].Char.UnicodeChar = ' ';
  584. }
  585. }
  586. int ccol, crow;
  587. public override void Move (int col, int row)
  588. {
  589. ccol = col;
  590. crow = row;
  591. }
  592. public override void AddRune (Rune rune)
  593. {
  594. var position = crow * Cols + ccol;
  595. if (Clip.Contains (ccol, crow))
  596. {
  597. OutputBuffer[position].Attributes = (ushort)currentAttribute;
  598. OutputBuffer[position].Char.UnicodeChar = (char)rune;
  599. }
  600. ccol++;
  601. if (ccol == Cols) {
  602. ccol = 0;
  603. if (crow + 1 < Rows)
  604. crow++;
  605. }
  606. if (sync)
  607. UpdateScreen ();
  608. }
  609. public override void AddStr (ustring str)
  610. {
  611. foreach (var rune in str)
  612. AddRune (rune);
  613. }
  614. int currentAttribute;
  615. public override void SetAttribute (Attribute c)
  616. {
  617. currentAttribute = c.value;
  618. }
  619. private Attribute MakeColor (ConsoleColor f, ConsoleColor b)
  620. {
  621. // Encode the colors into the int value.
  622. return new Attribute ()
  623. {
  624. value = ((int)f | (int)b << 4)
  625. };
  626. }
  627. public override void Refresh()
  628. {
  629. var bufferCoords = new WindowsConsole.Coord ()
  630. {
  631. X = (short)Clip.Width,
  632. Y = (short)Clip.Height
  633. };
  634. var window = new WindowsConsole.SmallRect ()
  635. {
  636. Top = 0,
  637. Left = 0,
  638. Right = (short)Clip.Right,
  639. Bottom = (short)Clip.Bottom
  640. };
  641. UpdateCursor();
  642. WinConsole.WriteToConsole (OutputBuffer, bufferCoords, window);
  643. }
  644. public override void UpdateScreen ()
  645. {
  646. var bufferCoords = new WindowsConsole.Coord ()
  647. {
  648. X = (short)Clip.Width,
  649. Y = (short)Clip.Height
  650. };
  651. var window = new WindowsConsole.SmallRect ()
  652. {
  653. Top = 0,
  654. Left = 0,
  655. Right = (short)Clip.Right,
  656. Bottom = (short)Clip.Bottom
  657. };
  658. UpdateCursor();
  659. WinConsole.WriteToConsole (OutputBuffer, bufferCoords, window);
  660. }
  661. public override void UpdateCursor()
  662. {
  663. var position = new WindowsConsole.Coord()
  664. {
  665. X = (short)ccol,
  666. Y = (short)crow
  667. };
  668. WinConsole.SetCursorPosition(position);
  669. }
  670. public override void End ()
  671. {
  672. WinConsole.Cleanup();
  673. }
  674. #region Unused
  675. public override void SetColors (ConsoleColor foreground, ConsoleColor background)
  676. {
  677. }
  678. public override void SetColors (short foregroundColorId, short backgroundColorId)
  679. {
  680. }
  681. public override void Suspend ()
  682. {
  683. }
  684. public override void StartReportingMouseMoves ()
  685. {
  686. }
  687. public override void StopReportingMouseMoves ()
  688. {
  689. }
  690. public override void UncookMouse ()
  691. {
  692. }
  693. public override void CookMouse ()
  694. {
  695. }
  696. #endregion
  697. }
  698. }