ConsoleDriver.cs 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121
  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. 0, // 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="Frame.OnDrawFrames"/> 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="Frame.OnDrawFrames"/> 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. /// Suspends the application (e.g. on Linux via SIGTSTP) and upon resume, resets the console driver.
  495. /// </summary>
  496. /// <remarks>This is only implemented in <see cref="CursesDriver"/>.</remarks>
  497. public abstract void Suspend ();
  498. // TODO: Move FillRect to ./Drawing
  499. /// <summary>
  500. /// Fills the specified rectangle with the specified rune.
  501. /// </summary>
  502. /// <param name="rect"></param>
  503. /// <param name="rune"></param>
  504. public void FillRect (Rect rect, Rune rune = default)
  505. {
  506. for (int r = rect.Y; r < rect.Y + rect.Height; r++) {
  507. for (int c = rect.X; c < rect.X + rect.Width; c++) {
  508. Application.Driver.Move (c, r);
  509. Application.Driver.AddRune (rune == default ? new Rune (' ') : rune);
  510. }
  511. }
  512. }
  513. /// <summary>
  514. /// Fills the specified rectangle with the specified <see langword="char"/>. This method
  515. /// is a convenience method that calls <see cref="FillRect(Rect, Rune)"/>.
  516. /// </summary>
  517. /// <param name="rect"></param>
  518. /// <param name="c"></param>
  519. public void FillRect (Rect rect, char c) => FillRect (rect, new Rune (c));
  520. /// <summary>
  521. /// Returns the name of the driver and relevant library version information.
  522. /// </summary>
  523. /// <returns></returns>
  524. public virtual string GetVersionInfo () => GetType ().Name;
  525. }
  526. /// <summary>
  527. /// Terminal Cursor Visibility settings.
  528. /// </summary>
  529. /// <remarks>
  530. /// Hex value are set as 0xAABBCCDD where :
  531. ///
  532. /// AA stand for the TERMINFO DECSUSR parameter value to be used under Linux and MacOS
  533. /// BB stand for the NCurses curs_set parameter value to be used under Linux and MacOS
  534. /// CC stand for the CONSOLE_CURSOR_INFO.bVisible parameter value to be used under Windows
  535. /// DD stand for the CONSOLE_CURSOR_INFO.dwSize parameter value to be used under Windows
  536. ///</remarks>
  537. public enum CursorVisibility {
  538. /// <summary>
  539. /// Cursor caret has default
  540. /// </summary>
  541. /// <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>
  542. Default = 0x00010119,
  543. /// <summary>
  544. /// Cursor caret is hidden
  545. /// </summary>
  546. Invisible = 0x03000019,
  547. /// <summary>
  548. /// Cursor caret is normally shown as a blinking underline bar _
  549. /// </summary>
  550. Underline = 0x03010119,
  551. /// <summary>
  552. /// Cursor caret is normally shown as a underline bar _
  553. /// </summary>
  554. /// <remarks>Under Windows, this is equivalent to <see ref="UnderscoreBlinking"/></remarks>
  555. UnderlineFix = 0x04010119,
  556. /// <summary>
  557. /// Cursor caret is displayed a blinking vertical bar |
  558. /// </summary>
  559. /// <remarks>Works under Xterm-like terminal otherwise this is equivalent to <see ref="Underscore"/></remarks>
  560. Vertical = 0x05010119,
  561. /// <summary>
  562. /// Cursor caret is displayed a blinking vertical bar |
  563. /// </summary>
  564. /// <remarks>Works under Xterm-like terminal otherwise this is equivalent to <see ref="Underscore"/></remarks>
  565. VerticalFix = 0x06010119,
  566. /// <summary>
  567. /// Cursor caret is displayed as a blinking block ▉
  568. /// </summary>
  569. Box = 0x01020164,
  570. /// <summary>
  571. /// Cursor caret is displayed a block ▉
  572. /// </summary>
  573. /// <remarks>Works under Xterm-like terminal otherwise this is equivalent to <see ref="Block"/></remarks>
  574. BoxFix = 0x02020164
  575. }
  576. /// <summary>
  577. /// The <see cref="KeyCode"/> enumeration encodes key information from <see cref="ConsoleDriver"/>s and provides a consistent
  578. /// way for application code to specify keys and receive key events.
  579. /// <para>
  580. /// The <see cref="Key"/> class provides a higher-level abstraction, with helper methods and properties for common
  581. /// operations. For example, <see cref="Key.IsAlt"/> and <see cref="Key.IsCtrl"/> provide a convenient way
  582. /// to check whether the Alt or Ctrl modifier keys were pressed when a key was pressed.
  583. /// </para>
  584. /// </summary>
  585. /// <remarks>
  586. /// <para>
  587. /// 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
  588. /// 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
  589. /// values for uppercase characters, these enum values represent *lowercase*, un-shifted characters.
  590. /// </para>
  591. /// <para>
  592. /// 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.).
  593. /// </para>
  594. /// <para>
  595. /// The shift modifiers (<see cref="KeyCode.ShiftMask"/>, <see cref="KeyCode.CtrlMask"/>, and <see cref="KeyCode.AltMask"/>) can be combined (with logical or)
  596. /// 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
  597. /// <see cref="KeyCode.ShiftMask"/> | <see cref="KeyCode.A"/> represents the 'A' key (shifted 'a' key). Likewise, <see cref="KeyCode.AltMask"/> | <see cref="KeyCode.A"/>
  598. /// represents the 'Alt+A' key combination.
  599. /// </para>
  600. /// <para>
  601. /// All other keys that produce a printable character are encoded as the Unicode value of the character. For example, the <see cref="KeyCode"/>
  602. /// for the '!' character is 33, which is the Unicode value for '!'. Likewise, `â` is 226, `Â` is 194, etc.
  603. /// </para>
  604. /// <para>
  605. /// If the <see cref="SpecialMask"/> is set, then the value is that of the special mask,
  606. /// otherwise, the value is the one of the lower bits (as extracted by <see cref="CharMask"/>).
  607. /// </para>
  608. /// </remarks>
  609. [Flags]
  610. public enum KeyCode : uint {
  611. /// <summary>
  612. /// Mask that indicates that the key is a unicode codepoint. Values outside this range
  613. /// indicate the key has shift modifiers or is a special key like function keys, arrows keys and so on.
  614. /// </summary>
  615. CharMask = 0x_f_ffff,
  616. /// <summary>
  617. /// If the <see cref="SpecialMask"/> is set, then the value is that of the special mask,
  618. /// otherwise, the value is in the the lower bits (as extracted by <see cref="CharMask"/>).
  619. /// </summary>
  620. SpecialMask = 0x_fff0_0000,
  621. /// <summary>
  622. /// When this value is set, the Key encodes the sequence Shift-KeyValue.
  623. /// The actual value must be extracted by removing the ShiftMask.
  624. /// </summary>
  625. ShiftMask = 0x_1000_0000,
  626. /// <summary>
  627. /// When this value is set, the Key encodes the sequence Alt-KeyValue.
  628. /// The actual value must be extracted by removing the AltMask.
  629. /// </summary>
  630. AltMask = 0x_8000_0000,
  631. /// <summary>
  632. /// When this value is set, the Key encodes the sequence Ctrl-KeyValue.
  633. /// The actual value must be extracted by removing the CtrlMask.
  634. /// </summary>
  635. CtrlMask = 0x_4000_0000,
  636. /// <summary>
  637. /// The key code representing an invalid or empty key.
  638. /// </summary>
  639. Null = 0,
  640. /// <summary>
  641. /// Backspace key.
  642. /// </summary>
  643. Backspace = 8,
  644. /// <summary>
  645. /// The key code for the tab key (forwards tab key).
  646. /// </summary>
  647. Tab = 9,
  648. /// <summary>
  649. /// The key code for the return key.
  650. /// </summary>
  651. Enter = ConsoleKey.Enter,
  652. /// <summary>
  653. /// The key code for the clear key.
  654. /// </summary>
  655. Clear = 12,
  656. /// <summary>
  657. /// The key code for the escape key.
  658. /// </summary>
  659. Esc = 27,
  660. /// <summary>
  661. /// The key code for the space bar key.
  662. /// </summary>
  663. Space = 32,
  664. /// <summary>
  665. /// Digit 0.
  666. /// </summary>
  667. D0 = 48,
  668. /// <summary>
  669. /// Digit 1.
  670. /// </summary>
  671. D1,
  672. /// <summary>
  673. /// Digit 2.
  674. /// </summary>
  675. D2,
  676. /// <summary>
  677. /// Digit 3.
  678. /// </summary>
  679. D3,
  680. /// <summary>
  681. /// Digit 4.
  682. /// </summary>
  683. D4,
  684. /// <summary>
  685. /// Digit 5.
  686. /// </summary>
  687. D5,
  688. /// <summary>
  689. /// Digit 6.
  690. /// </summary>
  691. D6,
  692. /// <summary>
  693. /// Digit 7.
  694. /// </summary>
  695. D7,
  696. /// <summary>
  697. /// Digit 8.
  698. /// </summary>
  699. D8,
  700. /// <summary>
  701. /// Digit 9.
  702. /// </summary>
  703. D9,
  704. /// <summary>
  705. /// The key code for the A key
  706. /// </summary>
  707. A = 65,
  708. /// <summary>
  709. /// The key code for the B key
  710. /// </summary>
  711. B,
  712. /// <summary>
  713. /// The key code for the C key
  714. /// </summary>
  715. C,
  716. /// <summary>
  717. /// The key code for the D key
  718. /// </summary>
  719. D,
  720. /// <summary>
  721. /// The key code for the E key
  722. /// </summary>
  723. E,
  724. /// <summary>
  725. /// The key code for the F key
  726. /// </summary>
  727. F,
  728. /// <summary>
  729. /// The key code for the G key
  730. /// </summary>
  731. G,
  732. /// <summary>
  733. /// The key code for the H key
  734. /// </summary>
  735. H,
  736. /// <summary>
  737. /// The key code for the I key
  738. /// </summary>
  739. I,
  740. /// <summary>
  741. /// The key code for the J key
  742. /// </summary>
  743. J,
  744. /// <summary>
  745. /// The key code for the K key
  746. /// </summary>
  747. K,
  748. /// <summary>
  749. /// The key code for the L key
  750. /// </summary>
  751. L,
  752. /// <summary>
  753. /// The key code for the M key
  754. /// </summary>
  755. M,
  756. /// <summary>
  757. /// The key code for the N key
  758. /// </summary>
  759. N,
  760. /// <summary>
  761. /// The key code for the O key
  762. /// </summary>
  763. O,
  764. /// <summary>
  765. /// The key code for the P key
  766. /// </summary>
  767. P,
  768. /// <summary>
  769. /// The key code for the Q key
  770. /// </summary>
  771. Q,
  772. /// <summary>
  773. /// The key code for the R key
  774. /// </summary>
  775. R,
  776. /// <summary>
  777. /// The key code for the S key
  778. /// </summary>
  779. S,
  780. /// <summary>
  781. /// The key code for the T key
  782. /// </summary>
  783. T,
  784. /// <summary>
  785. /// The key code for the U key
  786. /// </summary>
  787. U,
  788. /// <summary>
  789. /// The key code for the V key
  790. /// </summary>
  791. V,
  792. /// <summary>
  793. /// The key code for the W key
  794. /// </summary>
  795. W,
  796. /// <summary>
  797. /// The key code for the X key
  798. /// </summary>
  799. X,
  800. /// <summary>
  801. /// The key code for the Y key
  802. /// </summary>
  803. Y,
  804. /// <summary>
  805. /// The key code for the Z key
  806. /// </summary>
  807. Z,
  808. ///// <summary>
  809. ///// The key code for the Delete key.
  810. ///// </summary>
  811. //Delete = 127,
  812. // --- Special keys ---
  813. // The values below are common non-alphanum keys. Their values are
  814. // based on the .NET ConsoleKey values, which, in-turn are based on the
  815. // VK_ values from the Windows API.
  816. // We add MaxCodePoint to avoid conflicts with the Unicode values.
  817. /// <summary>
  818. /// The maximum Unicode codepoint value. Used to encode the non-alphanumeric control
  819. /// keys.
  820. /// </summary>
  821. MaxCodePoint = 0x10FFFF,
  822. /// <summary>
  823. /// Cursor up key
  824. /// </summary>
  825. CursorUp = MaxCodePoint + ConsoleKey.UpArrow,
  826. /// <summary>
  827. /// Cursor down key.
  828. /// </summary>
  829. CursorDown = MaxCodePoint + ConsoleKey.DownArrow,
  830. /// <summary>
  831. /// Cursor left key.
  832. /// </summary>
  833. CursorLeft = MaxCodePoint + ConsoleKey.LeftArrow,
  834. /// <summary>
  835. /// Cursor right key.
  836. /// </summary>
  837. CursorRight = MaxCodePoint + ConsoleKey.RightArrow,
  838. /// <summary>
  839. /// Page Up key.
  840. /// </summary>
  841. PageUp = MaxCodePoint + ConsoleKey.PageUp,
  842. /// <summary>
  843. /// Page Down key.
  844. /// </summary>
  845. PageDown = MaxCodePoint + ConsoleKey.PageDown,
  846. /// <summary>
  847. /// Home key.
  848. /// </summary>
  849. Home = MaxCodePoint + ConsoleKey.Home,
  850. /// <summary>
  851. /// End key.
  852. /// </summary>
  853. End = MaxCodePoint + ConsoleKey.End,
  854. /// <summary>
  855. /// Insert (INS) key.
  856. /// </summary>
  857. Insert = MaxCodePoint + ConsoleKey.Insert,
  858. /// <summary>
  859. /// Delete (DEL) key.
  860. /// </summary>
  861. Delete = MaxCodePoint + ConsoleKey.Delete,
  862. /// <summary>
  863. /// Print screen character key.
  864. /// </summary>
  865. PrintScreen = MaxCodePoint + ConsoleKey.PrintScreen,
  866. /// <summary>
  867. /// F1 key.
  868. /// </summary>
  869. F1 = MaxCodePoint + ConsoleKey.F1,
  870. /// <summary>
  871. /// F2 key.
  872. /// </summary>
  873. F2 = MaxCodePoint + ConsoleKey.F2,
  874. /// <summary>
  875. /// F3 key.
  876. /// </summary>
  877. F3 = MaxCodePoint + ConsoleKey.F3,
  878. /// <summary>
  879. /// F4 key.
  880. /// </summary>
  881. F4 = MaxCodePoint + ConsoleKey.F4,
  882. /// <summary>
  883. /// F5 key.
  884. /// </summary>
  885. F5 = MaxCodePoint + ConsoleKey.F5,
  886. /// <summary>
  887. /// F6 key.
  888. /// </summary>
  889. F6 = MaxCodePoint + ConsoleKey.F6,
  890. /// <summary>
  891. /// F7 key.
  892. /// </summary>
  893. F7 = MaxCodePoint + ConsoleKey.F7,
  894. /// <summary>
  895. /// F8 key.
  896. /// </summary>
  897. F8 = MaxCodePoint + ConsoleKey.F8,
  898. /// <summary>
  899. /// F9 key.
  900. /// </summary>
  901. F9 = MaxCodePoint + ConsoleKey.F9,
  902. /// <summary>
  903. /// F10 key.
  904. /// </summary>
  905. F10 = MaxCodePoint + ConsoleKey.F10,
  906. /// <summary>
  907. /// F11 key.
  908. /// </summary>
  909. F11 = MaxCodePoint + ConsoleKey.F11,
  910. /// <summary>
  911. /// F12 key.
  912. /// </summary>
  913. F12 = MaxCodePoint + ConsoleKey.F12,
  914. /// <summary>
  915. /// F13 key.
  916. /// </summary>
  917. F13 = MaxCodePoint + ConsoleKey.F13,
  918. /// <summary>
  919. /// F14 key.
  920. /// </summary>
  921. F14 = MaxCodePoint + ConsoleKey.F14,
  922. /// <summary>
  923. /// F15 key.
  924. /// </summary>
  925. F15 = MaxCodePoint + ConsoleKey.F15,
  926. /// <summary>
  927. /// F16 key.
  928. /// </summary>
  929. F16 = MaxCodePoint + ConsoleKey.F16,
  930. /// <summary>
  931. /// F17 key.
  932. /// </summary>
  933. F17 = MaxCodePoint + ConsoleKey.F17,
  934. /// <summary>
  935. /// F18 key.
  936. /// </summary>
  937. F18 = MaxCodePoint + ConsoleKey.F18,
  938. /// <summary>
  939. /// F19 key.
  940. /// </summary>
  941. F19 = MaxCodePoint + ConsoleKey.F19,
  942. /// <summary>
  943. /// F20 key.
  944. /// </summary>
  945. F20 = MaxCodePoint + ConsoleKey.F20,
  946. /// <summary>
  947. /// F21 key.
  948. /// </summary>
  949. F21 = MaxCodePoint + ConsoleKey.F21,
  950. /// <summary>
  951. /// F22 key.
  952. /// </summary>
  953. F22 = MaxCodePoint + ConsoleKey.F22,
  954. /// <summary>
  955. /// F23 key.
  956. /// </summary>
  957. F23 = MaxCodePoint + ConsoleKey.F23,
  958. /// <summary>
  959. /// F24 key.
  960. /// </summary>
  961. F24 = MaxCodePoint + ConsoleKey.F24,
  962. }