FakeMainLoop.cs 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. using System;
  2. using System.Threading;
  3. namespace Terminal.Gui {
  4. /// <summary>
  5. /// Mainloop intended to be used with the .NET System.Console API, and can
  6. /// be used on Windows and Unix, it is cross platform but lacks things like
  7. /// file descriptor monitoring.
  8. /// </summary>
  9. /// <remarks>
  10. /// This implementation is used for FakeDriver.
  11. /// </remarks>
  12. public class FakeMainLoop : IMainLoopDriver {
  13. AutoResetEvent keyReady = new AutoResetEvent (false);
  14. AutoResetEvent waitForProbe = new AutoResetEvent (false);
  15. ConsoleKeyInfo? keyResult = null;
  16. MainLoop mainLoop;
  17. Func<ConsoleKeyInfo> consoleKeyReaderFn = null;
  18. /// <summary>
  19. /// Invoked when a Key is pressed.
  20. /// </summary>
  21. public Action<ConsoleKeyInfo> KeyPressed;
  22. /// <summary>
  23. /// Initializes the class.
  24. /// </summary>
  25. /// <remarks>
  26. /// Passing a consoleKeyReaderfn is provided to support unit test scenarios.
  27. /// </remarks>
  28. /// <param name="consoleKeyReaderFn">The method to be called to get a key from the console.</param>
  29. public FakeMainLoop (Func<ConsoleKeyInfo> consoleKeyReaderFn = null)
  30. {
  31. if (consoleKeyReaderFn == null) {
  32. throw new ArgumentNullException ("key reader function must be provided.");
  33. }
  34. this.consoleKeyReaderFn = consoleKeyReaderFn;
  35. }
  36. void WindowsKeyReader ()
  37. {
  38. while (true) {
  39. waitForProbe.WaitOne ();
  40. keyResult = consoleKeyReaderFn ();
  41. keyReady.Set ();
  42. }
  43. }
  44. void IMainLoopDriver.Setup (MainLoop mainLoop)
  45. {
  46. this.mainLoop = mainLoop;
  47. Thread readThread = new Thread (WindowsKeyReader);
  48. readThread.Start ();
  49. }
  50. void IMainLoopDriver.Wakeup ()
  51. {
  52. }
  53. bool IMainLoopDriver.EventsPending (bool wait)
  54. {
  55. keyResult = null;
  56. waitForProbe.Set ();
  57. if (CheckTimers (wait, out var waitTimeout)) {
  58. return true;
  59. }
  60. keyReady.WaitOne (waitTimeout);
  61. return keyResult.HasValue;
  62. }
  63. bool CheckTimers (bool wait, out int waitTimeout)
  64. {
  65. long now = DateTime.UtcNow.Ticks;
  66. if (mainLoop.timeouts.Count > 0) {
  67. waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond);
  68. if (waitTimeout < 0)
  69. return true;
  70. } else {
  71. waitTimeout = -1;
  72. }
  73. if (!wait)
  74. waitTimeout = 0;
  75. int ic;
  76. lock (mainLoop.idleHandlers) {
  77. ic = mainLoop.idleHandlers.Count;
  78. }
  79. return ic > 0;
  80. }
  81. void IMainLoopDriver.MainIteration ()
  82. {
  83. if (keyResult.HasValue) {
  84. KeyPressed?.Invoke (keyResult.Value);
  85. keyResult = null;
  86. }
  87. }
  88. }
  89. }