NetOutput.cs 5.2 KB

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