ConsoleDriver.cs 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126
  1. //
  2. // ConsoleDriver.cs: Base class for Terminal.Gui ConsoleDriver implementations.
  3. //
  4. using System.Text;
  5. using System;
  6. using System.Diagnostics;
  7. using System.Linq;
  8. using Terminal.Gui.ConsoleDrivers;
  9. namespace Terminal.Gui;
  10. /// <summary>
  11. /// Base class for Terminal.Gui ConsoleDriver implementations.
  12. /// </summary>
  13. /// <remarks>
  14. /// There are currently four implementations:
  15. /// - <see cref="CursesDriver"/> (for Unix and Mac)
  16. /// - <see cref="WindowsDriver"/>
  17. /// - <see cref="NetDriver"/> that uses the .NET Console API
  18. /// - <see cref="FakeConsole"/> for unit testing.
  19. /// </remarks>
  20. public abstract class ConsoleDriver {
  21. /// <summary>
  22. /// Set this to true in any unit tests that attempt to test drivers other than FakeDriver.
  23. /// <code>
  24. /// public ColorTests ()
  25. /// {
  26. /// ConsoleDriver.RunningUnitTests = true;
  27. /// }
  28. /// </code>
  29. /// </summary>
  30. internal static bool RunningUnitTests { get; set; }
  31. #region Setup & Teardown
  32. /// <summary>
  33. /// Initializes the driver
  34. /// </summary>
  35. /// <returns>Returns an instance of <see cref="MainLoop"/> using the <see cref="IMainLoopDriver"/> for the driver.</returns>
  36. internal abstract MainLoop Init ();
  37. /// <summary>
  38. /// Ends the execution of the console driver.
  39. /// </summary>
  40. internal abstract void End ();
  41. #endregion
  42. /// <summary>
  43. /// The event fired when the terminal is resized.
  44. /// </summary>
  45. public event EventHandler<SizeChangedEventArgs> SizeChanged;
  46. /// <summary>
  47. /// Called when the terminal size changes. Fires the <see cref="SizeChanged"/> event.
  48. /// </summary>
  49. /// <param name="args"></param>
  50. public void OnSizeChanged (SizeChangedEventArgs args) => SizeChanged?.Invoke (this, args);
  51. /// <summary>
  52. /// The number of columns visible in the terminal.
  53. /// </summary>
  54. public virtual int Cols {
  55. get => _cols;
  56. internal set {
  57. _cols = value;
  58. ClearContents ();
  59. }
  60. }
  61. /// <summary>
  62. /// The number of rows visible in the terminal.
  63. /// </summary>
  64. public virtual int Rows {
  65. get => _rows;
  66. internal set {
  67. _rows = value;
  68. ClearContents ();
  69. }
  70. }
  71. /// <summary>
  72. /// The leftmost column in the terminal.
  73. /// </summary>
  74. public virtual int Left { get; internal set; } = 0;
  75. /// <summary>
  76. /// The topmost row in the terminal.
  77. /// </summary>
  78. public virtual int Top { get; internal set; } = 0;
  79. /// <summary>
  80. /// Get the operating system clipboard.
  81. /// </summary>
  82. public IClipboard Clipboard { get; internal set; }
  83. /// <summary>
  84. /// The contents of the application output. The driver outputs this buffer to the terminal when <see cref="UpdateScreen"/>
  85. /// is called.
  86. /// <remarks>
  87. /// The format of the array is rows, columns. The first index is the row, the second index is the column.
  88. /// </remarks>
  89. /// </summary>
  90. public Cell [,] Contents { get; internal set; }
  91. /// <summary>
  92. /// Gets the column last set by <see cref="Move"/>. <see cref="Col"/> and <see cref="Row"/>
  93. /// are used by <see cref="AddRune(Rune)"/> and <see cref="AddStr"/> to determine where to add content.
  94. /// </summary>
  95. public int Col { get; internal set; }
  96. /// <summary>
  97. /// Gets the row last set by <see cref="Move"/>. <see cref="Col"/> and <see cref="Row"/>
  98. /// are used by <see cref="AddRune(Rune)"/> and <see cref="AddStr"/> to determine where to add content.
  99. /// </summary>
  100. public int Row { get; internal set; }
  101. /// <summary>
  102. /// Updates <see cref="Col"/> and <see cref="Row"/> to the specified column and row in <see cref="Contents"/>.
  103. /// Used by <see cref="AddRune(Rune)"/> and <see cref="AddStr"/> to determine where to add content.
  104. /// </summary>
  105. /// <remarks>
  106. /// <para>
  107. /// This does not move the cursor on the screen, it only updates the internal state of the driver.
  108. /// </para>
  109. /// <para>
  110. /// If <paramref name="col"/> or <paramref name="row"/> are negative or beyond <see cref="Cols"/> and <see cref="Rows"/>,
  111. /// the method still sets those properties.
  112. /// </para>
  113. /// </remarks>
  114. /// <param name="col">Column to move to.</param>
  115. /// <param name="row">Row to move to.</param>
  116. public virtual void Move (int col, int row)
  117. {
  118. Col = col;
  119. Row = row;
  120. }
  121. /// <summary>
  122. /// Tests if the specified rune is supported by the driver.
  123. /// </summary>
  124. /// <param name="rune"></param>
  125. /// <returns><see langword="true"/> if the rune can be properly presented; <see langword="false"/> if the driver
  126. /// does not support displaying this rune.</returns>
  127. public virtual bool IsRuneSupported (Rune rune) => Rune.IsValid (rune.Value);
  128. /// <summary>
  129. /// Adds the specified rune to the display at the current cursor position.
  130. /// </summary>
  131. /// <remarks>
  132. /// <para>
  133. /// When the method returns, <see cref="Col"/> will be incremented by the number of columns <paramref name="rune"/> required,
  134. /// even if the new column value is outside of the <see cref="Clip"/> or screen dimensions defined by <see cref="Cols"/>.
  135. /// </para>
  136. /// <para>
  137. /// If <paramref name="rune"/> requires more than one column, and <see cref="Col"/> plus the number of columns needed
  138. /// exceeds the <see cref="Clip"/> or screen dimensions, the default Unicode replacement character (U+FFFD) will be added instead.
  139. /// </para>
  140. /// </remarks>
  141. /// <param name="rune">Rune to add.</param>
  142. public void AddRune (Rune rune)
  143. {
  144. int runeWidth = -1;
  145. bool validLocation = IsValidLocation (Col, Row);
  146. if (validLocation) {
  147. rune = rune.MakePrintable ();
  148. runeWidth = rune.GetColumns ();
  149. if (runeWidth == 0 && rune.IsCombiningMark ()) {
  150. // AtlasEngine does not support NON-NORMALIZED combining marks in a way
  151. // compatible with the driver architecture. Any CMs (except in the first col)
  152. // are correctly combined with the base char, but are ALSO treated as 1 column
  153. // width codepoints E.g. `echo "[e`u{0301}`u{0301}]"` will output `[é ]`.
  154. //
  155. // Until this is addressed (see Issue #), we do our best by
  156. // a) Attempting to normalize any CM with the base char to it's left
  157. // b) Ignoring any CMs that don't normalize
  158. if (Col > 0) {
  159. if (Contents [Row, Col - 1].CombiningMarks.Count > 0) {
  160. // Just add this mark to the list
  161. Contents [Row, Col - 1].CombiningMarks.Add (rune);
  162. // Ignore. Don't move to next column (let the driver figure out what to do).
  163. } else {
  164. // Attempt to normalize the cell to our left combined with this mark
  165. string combined = Contents [Row, Col - 1].Rune + rune.ToString ();
  166. // Normalize to Form C (Canonical Composition)
  167. string normalized = combined.Normalize (NormalizationForm.FormC);
  168. if (normalized.Length == 1) {
  169. // It normalized! We can just set the Cell to the left with the
  170. // normalized codepoint
  171. Contents [Row, Col - 1].Rune = (Rune)normalized [0];
  172. // Ignore. Don't move to next column because we're already there
  173. } else {
  174. // It didn't normalize. Add it to the Cell to left's CM list
  175. Contents [Row, Col - 1].CombiningMarks.Add (rune);
  176. // Ignore. Don't move to next column (let the driver figure out what to do).
  177. }
  178. }
  179. Contents [Row, Col - 1].Attribute = CurrentAttribute;
  180. Contents [Row, Col - 1].IsDirty = true;
  181. } else {
  182. // Most drivers will render a combining mark at col 0 as the mark
  183. Contents [Row, Col].Rune = rune;
  184. Contents [Row, Col].Attribute = CurrentAttribute;
  185. Contents [Row, Col].IsDirty = true;
  186. Col++;
  187. }
  188. } else {
  189. Contents [Row, Col].Attribute = CurrentAttribute;
  190. Contents [Row, Col].IsDirty = true;
  191. if (Col > 0) {
  192. // Check if cell to left has a wide glyph
  193. if (Contents [Row, Col - 1].Rune.GetColumns () > 1) {
  194. // Invalidate cell to left
  195. Contents [Row, Col - 1].Rune = Rune.ReplacementChar;
  196. Contents [Row, Col - 1].IsDirty = true;
  197. }
  198. }
  199. if (runeWidth < 1) {
  200. Contents [Row, Col].Rune = Rune.ReplacementChar;
  201. } else if (runeWidth == 1) {
  202. Contents [Row, Col].Rune = rune;
  203. if (Col < Clip.Right - 1) {
  204. Contents [Row, Col + 1].IsDirty = true;
  205. }
  206. } else if (runeWidth == 2) {
  207. if (Col == Clip.Right - 1) {
  208. // We're at the right edge of the clip, so we can't display a wide character.
  209. // TODO: Figure out if it is better to show a replacement character or ' '
  210. Contents [Row, Col].Rune = Rune.ReplacementChar;
  211. } else {
  212. Contents [Row, Col].Rune = rune;
  213. if (Col < Clip.Right - 1) {
  214. // Invalidate cell to right so that it doesn't get drawn
  215. // TODO: Figure out if it is better to show a replacement character or ' '
  216. Contents [Row, Col + 1].Rune = Rune.ReplacementChar;
  217. Contents [Row, Col + 1].IsDirty = true;
  218. }
  219. }
  220. } else {
  221. // This is a non-spacing character, so we don't need to do anything
  222. Contents [Row, Col].Rune = (Rune)' ';
  223. Contents [Row, Col].IsDirty = false;
  224. }
  225. _dirtyLines [Row] = true;
  226. }
  227. }
  228. if (runeWidth is < 0 or > 0) {
  229. Col++;
  230. }
  231. if (runeWidth > 1) {
  232. Debug.Assert (runeWidth <= 2);
  233. if (validLocation && Col < Clip.Right) {
  234. // This is a double-width character, and we are not at the end of the line.
  235. // Col now points to the second column of the character. Ensure it doesn't
  236. // Get rendered.
  237. Contents [Row, Col].IsDirty = false;
  238. Contents [Row, Col].Attribute = CurrentAttribute;
  239. // TODO: Determine if we should wipe this out (for now now)
  240. //Contents [Row, Col].Rune = (Rune)' ';
  241. }
  242. Col++;
  243. }
  244. }
  245. /// <summary>
  246. /// Adds the specified <see langword="char"/> to the display at the current cursor position. This method
  247. /// is a convenience method that calls <see cref="AddRune(Rune)"/> with the <see cref="Rune"/> constructor.
  248. /// </summary>
  249. /// <param name="c">Character to add.</param>
  250. public void AddRune (char c) => AddRune (new Rune (c));
  251. /// <summary>
  252. /// Adds the <paramref name="str"/> to the display at the cursor position.
  253. /// </summary>
  254. /// <remarks>
  255. /// <para>
  256. /// When the method returns, <see cref="Col"/> will be incremented by the number of columns <paramref name="str"/> required,
  257. /// unless the new column value is outside of the <see cref="Clip"/> or screen dimensions defined by <see cref="Cols"/>.
  258. /// </para>
  259. /// <para>
  260. /// If <paramref name="str"/> requires more columns than are available, the output will be clipped.
  261. /// </para>
  262. /// </remarks>
  263. /// <param name="str">String.</param>
  264. public void AddStr (string str)
  265. {
  266. var runes = str.EnumerateRunes ().ToList ();
  267. for (int i = 0; i < runes.Count; i++) {
  268. //if (runes [i].IsCombiningMark()) {
  269. // // Attempt to normalize
  270. // string combined = runes [i-1] + runes [i].ToString();
  271. // // Normalize to Form C (Canonical Composition)
  272. // string normalized = combined.Normalize (NormalizationForm.FormC);
  273. // runes [i-]
  274. //}
  275. AddRune (runes [i]);
  276. }
  277. }
  278. Rect _clip;
  279. /// <summary>
  280. /// Tests whether the specified coordinate are valid for drawing.
  281. /// </summary>
  282. /// <param name="col">The column.</param>
  283. /// <param name="row">The row.</param>
  284. /// <returns><see langword="false"/> if the coordinate is outside of the
  285. /// screen bounds or outside of <see cref="Clip"/>. <see langword="true"/> otherwise.</returns>
  286. public bool IsValidLocation (int col, int row) =>
  287. col >= 0 && row >= 0 &&
  288. col < Cols && row < Rows &&
  289. Clip.Contains (col, row);
  290. /// <summary>
  291. /// Gets or sets the clip rectangle that <see cref="AddRune(Rune)"/> and <see cref="AddStr(string)"/> are
  292. /// subject to.
  293. /// </summary>
  294. /// <value>The rectangle describing the bounds of <see cref="Clip"/>.</value>
  295. public Rect Clip {
  296. get => _clip;
  297. set => _clip = value;
  298. }
  299. /// <summary>
  300. /// Updates the screen to reflect all the changes that have been done to the display buffer
  301. /// </summary>
  302. public abstract void Refresh ();
  303. /// <summary>
  304. /// Sets the position of the terminal cursor to <see cref="Col"/> and <see cref="Row"/>.
  305. /// </summary>
  306. public abstract void UpdateCursor ();
  307. /// <summary>
  308. /// Gets the terminal cursor visibility.
  309. /// </summary>
  310. /// <param name="visibility">The current <see cref="CursorVisibility"/></param>
  311. /// <returns><see langword="true"/> upon success</returns>
  312. public abstract bool GetCursorVisibility (out CursorVisibility visibility);
  313. /// <summary>
  314. /// Sets the terminal cursor visibility.
  315. /// </summary>
  316. /// <param name="visibility">The wished <see cref="CursorVisibility"/></param>
  317. /// <returns><see langword="true"/> upon success</returns>
  318. public abstract bool SetCursorVisibility (CursorVisibility visibility);
  319. /// <summary>
  320. /// Determines if the terminal cursor should be visible or not and sets it accordingly.
  321. /// </summary>
  322. /// <returns><see langword="true"/> upon success</returns>
  323. public abstract bool EnsureCursorVisibility ();
  324. // As performance is a concern, we keep track of the dirty lines and only refresh those.
  325. // This is in addition to the dirty flag on each cell.
  326. internal bool [] _dirtyLines;
  327. /// <summary>
  328. /// Clears the <see cref="Contents"/> of the driver.
  329. /// </summary>
  330. public void ClearContents ()
  331. {
  332. // TODO: This method is really "Clear Contents" now and should not be abstract (or virtual)
  333. Contents = new Cell [Rows, Cols];
  334. Clip = new Rect (0, 0, Cols, Rows);
  335. _dirtyLines = new bool [Rows];
  336. lock (Contents) {
  337. // Can raise an exception while is still resizing.
  338. try {
  339. for (int row = 0; row < Rows; row++) {
  340. for (int c = 0; c < Cols; c++) {
  341. Contents [row, c] = new Cell () {
  342. Rune = (Rune)' ',
  343. Attribute = new Attribute (Color.White, Color.Black),
  344. IsDirty = true
  345. };
  346. _dirtyLines [row] = true;
  347. }
  348. }
  349. } catch (IndexOutOfRangeException) { }
  350. }
  351. }
  352. /// <summary>
  353. /// Redraws the physical screen with the contents that have been queued up via any of the printing commands.
  354. /// </summary>
  355. public abstract void UpdateScreen ();
  356. #region Color Handling
  357. /// <summary>
  358. /// Gets whether the <see cref="ConsoleDriver"/> supports TrueColor output.
  359. /// </summary>
  360. public virtual bool SupportsTrueColor => true;
  361. /// <summary>
  362. /// Gets or sets whether the <see cref="ConsoleDriver"/> should use 16 colors instead of the default TrueColors. See <see cref="Application.Force16Colors"/>
  363. /// to change this setting via <see cref="ConfigurationManager"/>.
  364. /// </summary>
  365. /// <remarks>
  366. /// <para>
  367. /// Will be forced to <see langword="true"/> if <see cref="ConsoleDriver.SupportsTrueColor"/> is <see langword="false"/>, indicating
  368. /// that the <see cref="ConsoleDriver"/> cannot support TrueColor.
  369. /// </para>
  370. /// </remarks>
  371. internal virtual bool Force16Colors {
  372. get => Application.Force16Colors || !SupportsTrueColor;
  373. set => Application.Force16Colors = value || !SupportsTrueColor;
  374. }
  375. Attribute _currentAttribute;
  376. int _cols;
  377. int _rows;
  378. /// <summary>
  379. /// The <see cref="Attribute"/> that will be used for the next <see cref="AddRune(Rune)"/> or <see cref="AddStr"/> call.
  380. /// </summary>
  381. public Attribute CurrentAttribute {
  382. get => _currentAttribute;
  383. set {
  384. if (Application.Driver != null) {
  385. _currentAttribute = new Attribute (value.Foreground, value.Background);
  386. return;
  387. }
  388. _currentAttribute = value;
  389. }
  390. }
  391. /// <summary>
  392. /// Selects the specified attribute as the attribute to use for future calls to AddRune and AddString.
  393. /// </summary>
  394. /// <remarks>
  395. /// Implementations should call <c>base.SetAttribute(c)</c>.
  396. /// </remarks>
  397. /// <param name="c">C.</param>
  398. public Attribute SetAttribute (Attribute c)
  399. {
  400. var prevAttribute = CurrentAttribute;
  401. CurrentAttribute = c;
  402. return prevAttribute;
  403. }
  404. /// <summary>
  405. /// Gets the current <see cref="Attribute"/>.
  406. /// </summary>
  407. /// <returns>The current attribute.</returns>
  408. public Attribute GetAttribute () => CurrentAttribute;
  409. // TODO: This is only overridden by CursesDriver. Once CursesDriver supports 24-bit color, this virtual method can be
  410. // removed (and Attribute can lose the platformColor property).
  411. /// <summary>
  412. /// Makes an <see cref="Attribute"/>.
  413. /// </summary>
  414. /// <param name="foreground">The foreground color.</param>
  415. /// <param name="background">The background color.</param>
  416. /// <returns>The attribute for the foreground and background colors.</returns>
  417. public virtual Attribute MakeColor (Color foreground, Color background) =>
  418. // Encode the colors into the int value.
  419. new (
  420. -1, // only used by cursesdriver!
  421. foreground,
  422. background
  423. );
  424. #endregion
  425. #region Mouse and Keyboard
  426. /// <summary>
  427. /// Event fired when a key is pressed down. This is a precursor to <see cref="KeyUp"/>.
  428. /// </summary>
  429. public event EventHandler<Key> KeyDown;
  430. /// <summary>
  431. /// Called when a key is pressed down. Fires the <see cref="KeyDown"/> event. This is a precursor to <see cref="OnKeyUp"/>.
  432. /// </summary>
  433. /// <param name="a"></param>
  434. public void OnKeyDown (Key a) => KeyDown?.Invoke (this, a);
  435. /// <summary>
  436. /// Event fired when a key is released.
  437. /// </summary>
  438. /// <remarks>
  439. /// Drivers that do not support key release events will fire this event after <see cref="KeyDown"/> processing is complete.
  440. /// </remarks>
  441. public event EventHandler<Key> KeyUp;
  442. /// <summary>
  443. /// Called when a key is released. Fires the <see cref="KeyUp"/> event.
  444. /// </summary>
  445. /// <remarks>
  446. /// Drivers that do not support key release events will calls this method after <see cref="OnKeyDown"/> processing is complete.
  447. /// </remarks>
  448. /// <param name="a"></param>
  449. public void OnKeyUp (Key a) => KeyUp?.Invoke (this, a);
  450. /// <summary>
  451. /// Event fired when a mouse event occurs.
  452. /// </summary>
  453. public event EventHandler<MouseEventEventArgs> MouseEvent;
  454. /// <summary>
  455. /// Called when a mouse event occurs. Fires the <see cref="MouseEvent"/> event.
  456. /// </summary>
  457. /// <param name="a"></param>
  458. public void OnMouseEvent (MouseEventEventArgs a) => MouseEvent?.Invoke (this, a);
  459. /// <summary>
  460. /// Simulates a key press.
  461. /// </summary>
  462. /// <param name="keyChar">The key character.</param>
  463. /// <param name="key">The key.</param>
  464. /// <param name="shift">If <see langword="true"/> simulates the Shift key being pressed.</param>
  465. /// <param name="alt">If <see langword="true"/> simulates the Alt key being pressed.</param>
  466. /// <param name="ctrl">If <see langword="true"/> simulates the Ctrl key being pressed.</param>
  467. public abstract void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool ctrl);
  468. #endregion
  469. /// <summary>
  470. /// Enables diagnostic functions
  471. /// </summary>
  472. [Flags]
  473. public enum DiagnosticFlags : uint {
  474. /// <summary>
  475. /// All diagnostics off
  476. /// </summary>
  477. Off = 0b_0000_0000,
  478. /// <summary>
  479. /// When enabled, <see cref="View.OnDrawAdornments"/> will draw a
  480. /// ruler in the frame for any side with a padding value greater than 0.
  481. /// </summary>
  482. FrameRuler = 0b_0000_0001,
  483. /// <summary>
  484. /// When enabled, <see cref="View.OnDrawAdornments"/> will draw a
  485. /// 'L', 'R', 'T', and 'B' when clearing <see cref="Thickness"/>'s instead of ' '.
  486. /// </summary>
  487. FramePadding = 0b_0000_0010
  488. }
  489. /// <summary>
  490. /// Set flags to enable/disable <see cref="ConsoleDriver"/> diagnostics.
  491. /// </summary>
  492. public static DiagnosticFlags Diagnostics { get; set; }
  493. /// <summary>
  494. /// Gets the dimensions of the terminal.
  495. /// </summary>
  496. public Rect Bounds => new Rect (0, 0, Cols, Rows);
  497. /// <summary>
  498. /// Suspends the application (e.g. on Linux via SIGTSTP) and upon resume, resets the console driver.
  499. /// </summary>
  500. /// <remarks>This is only implemented in <see cref="CursesDriver"/>.</remarks>
  501. public abstract void Suspend ();
  502. // TODO: Move FillRect to ./Drawing
  503. /// <summary>
  504. /// Fills the specified rectangle with the specified rune.
  505. /// </summary>
  506. /// <param name="rect"></param>
  507. /// <param name="rune"></param>
  508. public void FillRect (Rect rect, Rune rune = default)
  509. {
  510. for (int r = rect.Y; r < rect.Y + rect.Height; r++) {
  511. for (int c = rect.X; c < rect.X + rect.Width; c++) {
  512. Application.Driver.Move (c, r);
  513. Application.Driver.AddRune (rune == default ? new Rune (' ') : rune);
  514. }
  515. }
  516. }
  517. /// <summary>
  518. /// Fills the specified rectangle with the specified <see langword="char"/>. This method
  519. /// is a convenience method that calls <see cref="FillRect(Rect, Rune)"/>.
  520. /// </summary>
  521. /// <param name="rect"></param>
  522. /// <param name="c"></param>
  523. public void FillRect (Rect rect, char c) => FillRect (rect, new Rune (c));
  524. /// <summary>
  525. /// Returns the name of the driver and relevant library version information.
  526. /// </summary>
  527. /// <returns></returns>
  528. public virtual string GetVersionInfo () => GetType ().Name;
  529. }
  530. /// <summary>
  531. /// Terminal Cursor Visibility settings.
  532. /// </summary>
  533. /// <remarks>
  534. /// Hex value are set as 0xAABBCCDD where :
  535. ///
  536. /// AA stand for the TERMINFO DECSUSR parameter value to be used under Linux and MacOS
  537. /// BB stand for the NCurses curs_set parameter value to be used under Linux and MacOS
  538. /// CC stand for the CONSOLE_CURSOR_INFO.bVisible parameter value to be used under Windows
  539. /// DD stand for the CONSOLE_CURSOR_INFO.dwSize parameter value to be used under Windows
  540. ///</remarks>
  541. public enum CursorVisibility {
  542. /// <summary>
  543. /// Cursor caret has default
  544. /// </summary>
  545. /// <remarks>Works under Xterm-like terminal otherwise this is equivalent to <see ref="Underscore"/>. This default directly depends of the XTerm user configuration settings so it could be Block, I-Beam, Underline with possible blinking.</remarks>
  546. Default = 0x00010119,
  547. /// <summary>
  548. /// Cursor caret is hidden
  549. /// </summary>
  550. Invisible = 0x03000019,
  551. /// <summary>
  552. /// Cursor caret is normally shown as a blinking underline bar _
  553. /// </summary>
  554. Underline = 0x03010119,
  555. /// <summary>
  556. /// Cursor caret is normally shown as a underline bar _
  557. /// </summary>
  558. /// <remarks>Under Windows, this is equivalent to <see ref="UnderscoreBlinking"/></remarks>
  559. UnderlineFix = 0x04010119,
  560. /// <summary>
  561. /// Cursor caret is displayed a blinking vertical bar |
  562. /// </summary>
  563. /// <remarks>Works under Xterm-like terminal otherwise this is equivalent to <see ref="Underscore"/></remarks>
  564. Vertical = 0x05010119,
  565. /// <summary>
  566. /// Cursor caret is displayed a blinking vertical bar |
  567. /// </summary>
  568. /// <remarks>Works under Xterm-like terminal otherwise this is equivalent to <see ref="Underscore"/></remarks>
  569. VerticalFix = 0x06010119,
  570. /// <summary>
  571. /// Cursor caret is displayed as a blinking block ▉
  572. /// </summary>
  573. Box = 0x01020164,
  574. /// <summary>
  575. /// Cursor caret is displayed a block ▉
  576. /// </summary>
  577. /// <remarks>Works under Xterm-like terminal otherwise this is equivalent to <see ref="Block"/></remarks>
  578. BoxFix = 0x02020164
  579. }
  580. /// <summary>
  581. /// The <see cref="KeyCode"/> enumeration encodes key information from <see cref="ConsoleDriver"/>s and provides a consistent
  582. /// way for application code to specify keys and receive key events.
  583. /// <para>
  584. /// The <see cref="Key"/> class provides a higher-level abstraction, with helper methods and properties for common
  585. /// operations. For example, <see cref="Key.IsAlt"/> and <see cref="Key.IsCtrl"/> provide a convenient way
  586. /// to check whether the Alt or Ctrl modifier keys were pressed when a key was pressed.
  587. /// </para>
  588. /// </summary>
  589. /// <remarks>
  590. /// <para>
  591. /// Lowercase alpha keys are encoded as values between 65 and 90 corresponding to the un-shifted A to Z keys on a keyboard. Enum values
  592. /// are provided for these (e.g. <see cref="KeyCode.A"/>, <see cref="KeyCode.B"/>, etc.). Even though the values are the same as the ASCII
  593. /// values for uppercase characters, these enum values represent *lowercase*, un-shifted characters.
  594. /// </para>
  595. /// <para>
  596. /// Numeric keys are the values between 48 and 57 corresponding to 0 to 9 (e.g. <see cref="KeyCode.D0"/>, <see cref="KeyCode.D1"/>, etc.).
  597. /// </para>
  598. /// <para>
  599. /// The shift modifiers (<see cref="KeyCode.ShiftMask"/>, <see cref="KeyCode.CtrlMask"/>, and <see cref="KeyCode.AltMask"/>) can be combined (with logical or)
  600. /// with the other key codes to represent shifted keys. For example, the <see cref="KeyCode.A"/> enum value represents the un-shifted 'a' key, while
  601. /// <see cref="KeyCode.ShiftMask"/> | <see cref="KeyCode.A"/> represents the 'A' key (shifted 'a' key). Likewise, <see cref="KeyCode.AltMask"/> | <see cref="KeyCode.A"/>
  602. /// represents the 'Alt+A' key combination.
  603. /// </para>
  604. /// <para>
  605. /// All other keys that produce a printable character are encoded as the Unicode value of the character. For example, the <see cref="KeyCode"/>
  606. /// for the '!' character is 33, which is the Unicode value for '!'. Likewise, `â` is 226, `Â` is 194, etc.
  607. /// </para>
  608. /// <para>
  609. /// If the <see cref="SpecialMask"/> is set, then the value is that of the special mask,
  610. /// otherwise, the value is the one of the lower bits (as extracted by <see cref="CharMask"/>).
  611. /// </para>
  612. /// </remarks>
  613. [Flags]
  614. public enum KeyCode : uint {
  615. /// <summary>
  616. /// Mask that indicates that the key is a unicode codepoint. Values outside this range
  617. /// indicate the key has shift modifiers or is a special key like function keys, arrows keys and so on.
  618. /// </summary>
  619. CharMask = 0x_f_ffff,
  620. /// <summary>
  621. /// If the <see cref="SpecialMask"/> is set, then the value is that of the special mask,
  622. /// otherwise, the value is in the the lower bits (as extracted by <see cref="CharMask"/>).
  623. /// </summary>
  624. SpecialMask = 0x_fff0_0000,
  625. /// <summary>
  626. /// When this value is set, the Key encodes the sequence Shift-KeyValue.
  627. /// The actual value must be extracted by removing the ShiftMask.
  628. /// </summary>
  629. ShiftMask = 0x_1000_0000,
  630. /// <summary>
  631. /// When this value is set, the Key encodes the sequence Alt-KeyValue.
  632. /// The actual value must be extracted by removing the AltMask.
  633. /// </summary>
  634. AltMask = 0x_8000_0000,
  635. /// <summary>
  636. /// When this value is set, the Key encodes the sequence Ctrl-KeyValue.
  637. /// The actual value must be extracted by removing the CtrlMask.
  638. /// </summary>
  639. CtrlMask = 0x_4000_0000,
  640. /// <summary>
  641. /// The key code representing an invalid or empty key.
  642. /// </summary>
  643. Null = 0,
  644. /// <summary>
  645. /// Backspace key.
  646. /// </summary>
  647. Backspace = 8,
  648. /// <summary>
  649. /// The key code for the tab key (forwards tab key).
  650. /// </summary>
  651. Tab = 9,
  652. /// <summary>
  653. /// The key code for the return key.
  654. /// </summary>
  655. Enter = ConsoleKey.Enter,
  656. /// <summary>
  657. /// The key code for the clear key.
  658. /// </summary>
  659. Clear = 12,
  660. /// <summary>
  661. /// The key code for the escape key.
  662. /// </summary>
  663. Esc = 27,
  664. /// <summary>
  665. /// The key code for the space bar key.
  666. /// </summary>
  667. Space = 32,
  668. /// <summary>
  669. /// Digit 0.
  670. /// </summary>
  671. D0 = 48,
  672. /// <summary>
  673. /// Digit 1.
  674. /// </summary>
  675. D1,
  676. /// <summary>
  677. /// Digit 2.
  678. /// </summary>
  679. D2,
  680. /// <summary>
  681. /// Digit 3.
  682. /// </summary>
  683. D3,
  684. /// <summary>
  685. /// Digit 4.
  686. /// </summary>
  687. D4,
  688. /// <summary>
  689. /// Digit 5.
  690. /// </summary>
  691. D5,
  692. /// <summary>
  693. /// Digit 6.
  694. /// </summary>
  695. D6,
  696. /// <summary>
  697. /// Digit 7.
  698. /// </summary>
  699. D7,
  700. /// <summary>
  701. /// Digit 8.
  702. /// </summary>
  703. D8,
  704. /// <summary>
  705. /// Digit 9.
  706. /// </summary>
  707. D9,
  708. /// <summary>
  709. /// The key code for the A key
  710. /// </summary>
  711. A = 65,
  712. /// <summary>
  713. /// The key code for the B key
  714. /// </summary>
  715. B,
  716. /// <summary>
  717. /// The key code for the C key
  718. /// </summary>
  719. C,
  720. /// <summary>
  721. /// The key code for the D key
  722. /// </summary>
  723. D,
  724. /// <summary>
  725. /// The key code for the E key
  726. /// </summary>
  727. E,
  728. /// <summary>
  729. /// The key code for the F key
  730. /// </summary>
  731. F,
  732. /// <summary>
  733. /// The key code for the G key
  734. /// </summary>
  735. G,
  736. /// <summary>
  737. /// The key code for the H key
  738. /// </summary>
  739. H,
  740. /// <summary>
  741. /// The key code for the I key
  742. /// </summary>
  743. I,
  744. /// <summary>
  745. /// The key code for the J key
  746. /// </summary>
  747. J,
  748. /// <summary>
  749. /// The key code for the K key
  750. /// </summary>
  751. K,
  752. /// <summary>
  753. /// The key code for the L key
  754. /// </summary>
  755. L,
  756. /// <summary>
  757. /// The key code for the M key
  758. /// </summary>
  759. M,
  760. /// <summary>
  761. /// The key code for the N key
  762. /// </summary>
  763. N,
  764. /// <summary>
  765. /// The key code for the O key
  766. /// </summary>
  767. O,
  768. /// <summary>
  769. /// The key code for the P key
  770. /// </summary>
  771. P,
  772. /// <summary>
  773. /// The key code for the Q key
  774. /// </summary>
  775. Q,
  776. /// <summary>
  777. /// The key code for the R key
  778. /// </summary>
  779. R,
  780. /// <summary>
  781. /// The key code for the S key
  782. /// </summary>
  783. S,
  784. /// <summary>
  785. /// The key code for the T key
  786. /// </summary>
  787. T,
  788. /// <summary>
  789. /// The key code for the U key
  790. /// </summary>
  791. U,
  792. /// <summary>
  793. /// The key code for the V key
  794. /// </summary>
  795. V,
  796. /// <summary>
  797. /// The key code for the W key
  798. /// </summary>
  799. W,
  800. /// <summary>
  801. /// The key code for the X key
  802. /// </summary>
  803. X,
  804. /// <summary>
  805. /// The key code for the Y key
  806. /// </summary>
  807. Y,
  808. /// <summary>
  809. /// The key code for the Z key
  810. /// </summary>
  811. Z,
  812. ///// <summary>
  813. ///// The key code for the Delete key.
  814. ///// </summary>
  815. //Delete = 127,
  816. // --- Special keys ---
  817. // The values below are common non-alphanum keys. Their values are
  818. // based on the .NET ConsoleKey values, which, in-turn are based on the
  819. // VK_ values from the Windows API.
  820. // We add MaxCodePoint to avoid conflicts with the Unicode values.
  821. /// <summary>
  822. /// The maximum Unicode codepoint value. Used to encode the non-alphanumeric control
  823. /// keys.
  824. /// </summary>
  825. MaxCodePoint = 0x10FFFF,
  826. /// <summary>
  827. /// Cursor up key
  828. /// </summary>
  829. CursorUp = MaxCodePoint + ConsoleKey.UpArrow,
  830. /// <summary>
  831. /// Cursor down key.
  832. /// </summary>
  833. CursorDown = MaxCodePoint + ConsoleKey.DownArrow,
  834. /// <summary>
  835. /// Cursor left key.
  836. /// </summary>
  837. CursorLeft = MaxCodePoint + ConsoleKey.LeftArrow,
  838. /// <summary>
  839. /// Cursor right key.
  840. /// </summary>
  841. CursorRight = MaxCodePoint + ConsoleKey.RightArrow,
  842. /// <summary>
  843. /// Page Up key.
  844. /// </summary>
  845. PageUp = MaxCodePoint + ConsoleKey.PageUp,
  846. /// <summary>
  847. /// Page Down key.
  848. /// </summary>
  849. PageDown = MaxCodePoint + ConsoleKey.PageDown,
  850. /// <summary>
  851. /// Home key.
  852. /// </summary>
  853. Home = MaxCodePoint + ConsoleKey.Home,
  854. /// <summary>
  855. /// End key.
  856. /// </summary>
  857. End = MaxCodePoint + ConsoleKey.End,
  858. /// <summary>
  859. /// Insert (INS) key.
  860. /// </summary>
  861. Insert = MaxCodePoint + ConsoleKey.Insert,
  862. /// <summary>
  863. /// Delete (DEL) key.
  864. /// </summary>
  865. Delete = MaxCodePoint + ConsoleKey.Delete,
  866. /// <summary>
  867. /// Print screen character key.
  868. /// </summary>
  869. PrintScreen = MaxCodePoint + ConsoleKey.PrintScreen,
  870. /// <summary>
  871. /// F1 key.
  872. /// </summary>
  873. F1 = MaxCodePoint + ConsoleKey.F1,
  874. /// <summary>
  875. /// F2 key.
  876. /// </summary>
  877. F2 = MaxCodePoint + ConsoleKey.F2,
  878. /// <summary>
  879. /// F3 key.
  880. /// </summary>
  881. F3 = MaxCodePoint + ConsoleKey.F3,
  882. /// <summary>
  883. /// F4 key.
  884. /// </summary>
  885. F4 = MaxCodePoint + ConsoleKey.F4,
  886. /// <summary>
  887. /// F5 key.
  888. /// </summary>
  889. F5 = MaxCodePoint + ConsoleKey.F5,
  890. /// <summary>
  891. /// F6 key.
  892. /// </summary>
  893. F6 = MaxCodePoint + ConsoleKey.F6,
  894. /// <summary>
  895. /// F7 key.
  896. /// </summary>
  897. F7 = MaxCodePoint + ConsoleKey.F7,
  898. /// <summary>
  899. /// F8 key.
  900. /// </summary>
  901. F8 = MaxCodePoint + ConsoleKey.F8,
  902. /// <summary>
  903. /// F9 key.
  904. /// </summary>
  905. F9 = MaxCodePoint + ConsoleKey.F9,
  906. /// <summary>
  907. /// F10 key.
  908. /// </summary>
  909. F10 = MaxCodePoint + ConsoleKey.F10,
  910. /// <summary>
  911. /// F11 key.
  912. /// </summary>
  913. F11 = MaxCodePoint + ConsoleKey.F11,
  914. /// <summary>
  915. /// F12 key.
  916. /// </summary>
  917. F12 = MaxCodePoint + ConsoleKey.F12,
  918. /// <summary>
  919. /// F13 key.
  920. /// </summary>
  921. F13 = MaxCodePoint + ConsoleKey.F13,
  922. /// <summary>
  923. /// F14 key.
  924. /// </summary>
  925. F14 = MaxCodePoint + ConsoleKey.F14,
  926. /// <summary>
  927. /// F15 key.
  928. /// </summary>
  929. F15 = MaxCodePoint + ConsoleKey.F15,
  930. /// <summary>
  931. /// F16 key.
  932. /// </summary>
  933. F16 = MaxCodePoint + ConsoleKey.F16,
  934. /// <summary>
  935. /// F17 key.
  936. /// </summary>
  937. F17 = MaxCodePoint + ConsoleKey.F17,
  938. /// <summary>
  939. /// F18 key.
  940. /// </summary>
  941. F18 = MaxCodePoint + ConsoleKey.F18,
  942. /// <summary>
  943. /// F19 key.
  944. /// </summary>
  945. F19 = MaxCodePoint + ConsoleKey.F19,
  946. /// <summary>
  947. /// F20 key.
  948. /// </summary>
  949. F20 = MaxCodePoint + ConsoleKey.F20,
  950. /// <summary>
  951. /// F21 key.
  952. /// </summary>
  953. F21 = MaxCodePoint + ConsoleKey.F21,
  954. /// <summary>
  955. /// F22 key.
  956. /// </summary>
  957. F22 = MaxCodePoint + ConsoleKey.F22,
  958. /// <summary>
  959. /// F23 key.
  960. /// </summary>
  961. F23 = MaxCodePoint + ConsoleKey.F23,
  962. /// <summary>
  963. /// F24 key.
  964. /// </summary>
  965. F24 = MaxCodePoint + ConsoleKey.F24,
  966. }