NetOutput.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. using Microsoft.Extensions.Logging;
  2. namespace Terminal.Gui.Drivers;
  3. /// <summary>
  4. /// Implementation of <see cref="IOutput"/> that uses native dotnet
  5. /// methods e.g. <see cref="System.Console"/>
  6. /// </summary>
  7. public class NetOutput : OutputBase, IOutput
  8. {
  9. private readonly bool _isWinPlatform;
  10. /// <summary>
  11. /// Creates a new instance of the <see cref="NetOutput"/> class.
  12. /// </summary>
  13. public NetOutput ()
  14. {
  15. Logging.Information ($"Creating {nameof (NetOutput)}");
  16. try
  17. {
  18. Console.OutputEncoding = Encoding.UTF8;
  19. }
  20. catch
  21. {
  22. // ignore for unit tests
  23. }
  24. PlatformID p = Environment.OSVersion.Platform;
  25. if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows)
  26. {
  27. _isWinPlatform = true;
  28. }
  29. }
  30. /// <inheritdoc/>
  31. public void Write (ReadOnlySpan<char> text)
  32. {
  33. try
  34. {
  35. Console.Out.Write (text);
  36. }
  37. catch (IOException)
  38. {
  39. // Not connected to a terminal; do nothing
  40. }
  41. }
  42. /// <inheritdoc/>
  43. public Size GetSize ()
  44. {
  45. try
  46. {
  47. Size size = new (Console.WindowWidth, Console.WindowHeight);
  48. return size.IsEmpty ? new (80, 25) : size;
  49. }
  50. catch (IOException)
  51. {
  52. // Not connected to a terminal; return a default size
  53. return new (80, 25);
  54. }
  55. }
  56. /// <inheritdoc />
  57. public Point GetCursorPosition ()
  58. {
  59. return _lastCursorPosition ?? Point.Empty;
  60. }
  61. /// <inheritdoc/>
  62. public void SetCursorPosition (int col, int row) { SetCursorPositionImpl (col, row); }
  63. /// <inheritdoc />
  64. public void SetSize (int width, int height)
  65. {
  66. // Do Nothing.
  67. }
  68. private Point? _lastCursorPosition;
  69. /// <inheritdoc/>
  70. protected override void AppendOrWriteAttribute (StringBuilder output, Attribute attr, TextStyle redrawTextStyle)
  71. {
  72. if (Application.Force16Colors)
  73. {
  74. output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Foreground.GetAnsiColorCode ()));
  75. output.Append (EscSeqUtils.CSI_SetBackgroundColor (attr.Background.GetAnsiColorCode ()));
  76. }
  77. else
  78. {
  79. EscSeqUtils.CSI_AppendForegroundColorRGB (
  80. output,
  81. attr.Foreground.R,
  82. attr.Foreground.G,
  83. attr.Foreground.B
  84. );
  85. EscSeqUtils.CSI_AppendBackgroundColorRGB (
  86. output,
  87. attr.Background.R,
  88. attr.Background.G,
  89. attr.Background.B
  90. );
  91. }
  92. EscSeqUtils.CSI_AppendTextStyleChange (output, redrawTextStyle, attr.Style);
  93. }
  94. /// <inheritdoc />
  95. protected override void Write (StringBuilder output)
  96. {
  97. try
  98. {
  99. Console.Out.Write (output);
  100. }
  101. catch (IOException)
  102. {
  103. // Not connected to a terminal; do nothing
  104. }
  105. }
  106. /// <inheritdoc />
  107. protected override bool SetCursorPositionImpl (int col, int row)
  108. {
  109. if (_lastCursorPosition is { } && _lastCursorPosition.Value.X == col && _lastCursorPosition.Value.Y == row)
  110. {
  111. return true;
  112. }
  113. _lastCursorPosition = new (col, row);
  114. if (_isWinPlatform)
  115. {
  116. // Could happen that the windows is still resizing and the col is bigger than Console.WindowWidth.
  117. try
  118. {
  119. Console.SetCursorPosition (col, row);
  120. return true;
  121. }
  122. catch (Exception)
  123. {
  124. return false;
  125. }
  126. }
  127. // + 1 is needed because non-Windows is based on 1 instead of 0 and
  128. // Console.CursorTop/CursorLeft isn't reliable.
  129. EscSeqUtils.CSI_WriteCursorPosition (Console.Out, row + 1, col + 1);
  130. return true;
  131. }
  132. /// <inheritdoc/>
  133. public void Dispose ()
  134. {
  135. }
  136. private EscSeqUtils.DECSCUSR_Style? _currentDecscusrStyle;
  137. /// <inheritdoc cref="IOutput.SetCursorVisibility"/>
  138. public override void SetCursorVisibility (CursorVisibility visibility)
  139. {
  140. try
  141. {
  142. if (visibility != CursorVisibility.Invisible)
  143. {
  144. if (_currentDecscusrStyle is null || _currentDecscusrStyle != (EscSeqUtils.DECSCUSR_Style)(((int)visibility >> 24) & 0xFF))
  145. {
  146. _currentDecscusrStyle = (EscSeqUtils.DECSCUSR_Style)(((int)visibility >> 24) & 0xFF);
  147. Write (EscSeqUtils.CSI_SetCursorStyle ((EscSeqUtils.DECSCUSR_Style)_currentDecscusrStyle));
  148. }
  149. Write (EscSeqUtils.CSI_ShowCursor);
  150. }
  151. else
  152. {
  153. Write (EscSeqUtils.CSI_HideCursor);
  154. }
  155. }
  156. catch
  157. {
  158. // Ignore any exceptions
  159. }
  160. }
  161. }