NetInput.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. using Microsoft.Extensions.Logging;
  2. namespace Terminal.Gui.Drivers;
  3. /// <summary>
  4. /// <see cref="IInput{TInputRecord}"/> implementation that uses native dotnet methods e.g. <see cref="System.Console"/>.
  5. /// The <see cref="Peek"/> and <see cref="Read"/> methods are executed
  6. /// on the input thread created by <see cref="MainLoopCoordinator{TInputRecord}.StartInputTaskAsync"/>.
  7. /// </summary>
  8. public class NetInput : InputImpl<ConsoleKeyInfo>, ITestableInput<ConsoleKeyInfo>, IDisposable
  9. {
  10. /// <summary>
  11. /// Creates a new instance of the class. Implicitly sends
  12. /// console mode settings that enable virtual input (mouse
  13. /// reporting etc).
  14. /// </summary>
  15. public NetInput ()
  16. {
  17. Logging.Information ($"Creating {nameof (NetInput)}");
  18. PlatformID p = Environment.OSVersion.Platform;
  19. if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows)
  20. {
  21. try
  22. {
  23. _adjustConsole = new ();
  24. }
  25. catch (ApplicationException ex)
  26. {
  27. // Likely running as a unit test, or in a non-interactive session.
  28. Logging.Critical ($"NetWinVTConsole could not configure terminal modes. May indicate running in non-interactive session: {ex}");
  29. return;
  30. }
  31. }
  32. try
  33. {
  34. //Enable alternative screen buffer.
  35. Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll);
  36. //Set cursor key to application.
  37. Console.Out.Write (EscSeqUtils.CSI_HideCursor);
  38. Console.Out.Write (EscSeqUtils.CSI_EnableMouseEvents);
  39. Console.TreatControlCAsInput = true;
  40. }
  41. catch
  42. {
  43. // Swallow any exceptions during initialization for unit tests
  44. }
  45. }
  46. private readonly NetWinVTConsole _adjustConsole;
  47. /// <inheritdoc/>
  48. public override void Dispose ()
  49. {
  50. base.Dispose ();
  51. try
  52. {
  53. // Disable mouse events first
  54. Console.Out.Write (EscSeqUtils.CSI_DisableMouseEvents);
  55. //Disable alternative screen buffer.
  56. Console.Out.Write (EscSeqUtils.CSI_RestoreCursorAndRestoreAltBufferWithBackscroll);
  57. //Set cursor key to cursor.
  58. Console.Out.Write (EscSeqUtils.CSI_ShowCursor);
  59. _adjustConsole?.Cleanup ();
  60. // Flush any pending input so no stray events appear
  61. FlushConsoleInput ();
  62. }
  63. catch
  64. {
  65. // Swallow any exceptions during Dispose for unit tests
  66. }
  67. }
  68. /// <inheritdoc />
  69. public void AddInput (ConsoleKeyInfo input) { throw new NotImplementedException (); }
  70. /// <inheritdoc/>
  71. public override bool Peek ()
  72. {
  73. try
  74. {
  75. return Console.KeyAvailable;
  76. }
  77. catch
  78. {
  79. return false;
  80. }
  81. }
  82. /// <inheritdoc/>
  83. public override IEnumerable<ConsoleKeyInfo> Read ()
  84. {
  85. while (true)
  86. {
  87. ConsoleKeyInfo keyInfo = default;
  88. try
  89. {
  90. if (!Console.KeyAvailable)
  91. {
  92. break;
  93. }
  94. keyInfo = Console.ReadKey (true);
  95. }
  96. catch (InvalidOperationException)
  97. {
  98. // Not connected to a terminal (GitHub Actions, redirected input, etc.)
  99. yield break;
  100. }
  101. catch (IOException)
  102. {
  103. // I/O error reading from console
  104. yield break;
  105. }
  106. yield return keyInfo;
  107. }
  108. }
  109. private void FlushConsoleInput ()
  110. {
  111. while (Console.KeyAvailable)
  112. {
  113. Console.ReadKey (true);
  114. }
  115. }
  116. }