MainLoop.cs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. #nullable enable
  2. //
  3. // MainLoop.cs: IMainLoopDriver and MainLoop for Terminal.Gui
  4. //
  5. // Authors:
  6. // Miguel de Icaza ([email protected])
  7. //
  8. using System.Collections.ObjectModel;
  9. namespace Terminal.Gui.App;
  10. /// <summary>Interface to create a platform specific <see cref="MainLoop"/> driver.</summary>
  11. internal interface IMainLoopDriver
  12. {
  13. /// <summary>Must report whether there are any events pending, or even block waiting for events.</summary>
  14. /// <returns><see langword="true"/>, if there were pending events, <see langword="false"/> otherwise.</returns>
  15. bool EventsPending ();
  16. /// <summary>The iteration function.</summary>
  17. void Iteration ();
  18. /// <summary>Initializes the <see cref="MainLoop"/>, gets the calling main loop for the initialization.</summary>
  19. /// <remarks>Call <see cref="TearDown"/> to release resources.</remarks>
  20. /// <param name="mainLoop">Main loop.</param>
  21. void Setup (MainLoop mainLoop);
  22. /// <summary>Tears down the <see cref="MainLoop"/> driver. Releases resources created in <see cref="Setup"/>.</summary>
  23. void TearDown ();
  24. /// <summary>Wakes up the <see cref="MainLoop"/> that might be waiting on input, must be thread safe.</summary>
  25. void Wakeup ();
  26. }
  27. /// <summary>The main event loop of v1 driver based applications.</summary>
  28. /// <remarks>
  29. /// Monitoring of file descriptors is only available on Unix, there does not seem to be a way of supporting this
  30. /// on Windows.
  31. /// </remarks>
  32. public class MainLoop : IDisposable
  33. {
  34. /// <summary>
  35. /// Gets the class responsible for handling timeouts
  36. /// </summary>
  37. public ITimedEvents TimedEvents { get; } = new TimedEvents();
  38. /// <summary>Creates a new MainLoop.</summary>
  39. /// <remarks>Use <see cref="Dispose"/> to release resources.</remarks>
  40. /// <param name="driver">
  41. /// The <see cref="IConsoleDriver"/> instance (one of the implementations FakeMainLoop, UnixMainLoop,
  42. /// NetMainLoop or WindowsMainLoop).
  43. /// </param>
  44. internal MainLoop (IMainLoopDriver driver)
  45. {
  46. MainLoopDriver = driver;
  47. driver.Setup (this);
  48. }
  49. /// <summary>The current <see cref="IMainLoopDriver"/> in use.</summary>
  50. /// <value>The main loop driver.</value>
  51. internal IMainLoopDriver? MainLoopDriver { get; private set; }
  52. /// <summary>Used for unit tests.</summary>
  53. internal bool Running { get; set; }
  54. /// <inheritdoc/>
  55. public void Dispose ()
  56. {
  57. GC.SuppressFinalize (this);
  58. Stop ();
  59. Running = false;
  60. MainLoopDriver?.TearDown ();
  61. MainLoopDriver = null;
  62. }
  63. /// <summary>Determines whether there are pending events to be processed.</summary>
  64. /// <remarks>
  65. /// You can use this method if you want to probe if events are pending. Typically used if you need to flush the
  66. /// input queue while still running some of your own code in your main thread.
  67. /// </remarks>
  68. internal bool EventsPending () { return MainLoopDriver!.EventsPending (); }
  69. /// <summary>Runs the <see cref="MainLoop"/>. Used only for unit tests.</summary>
  70. internal void Run ()
  71. {
  72. bool prev = Running;
  73. Running = true;
  74. while (Running)
  75. {
  76. EventsPending ();
  77. RunIteration ();
  78. }
  79. Running = prev;
  80. }
  81. /// <summary>Runs one iteration of timers and file watches</summary>
  82. /// <remarks>
  83. /// Use this to process all pending events (timers handlers and file watches).
  84. /// <code>
  85. /// while (main.EventsPending ()) RunIteration ();
  86. /// </code>
  87. /// </remarks>
  88. internal void RunIteration ()
  89. {
  90. RunAnsiScheduler ();
  91. MainLoopDriver?.Iteration ();
  92. TimedEvents.RunTimers ();
  93. }
  94. private void RunAnsiScheduler ()
  95. {
  96. Application.Driver?.GetRequestScheduler ().RunSchedule ();
  97. }
  98. /// <summary>Stops the main loop driver and calls <see cref="IMainLoopDriver.Wakeup"/>. Used only for unit tests.</summary>
  99. internal void Stop ()
  100. {
  101. Running = false;
  102. Wakeup ();
  103. }
  104. /// <summary>Wakes up the <see cref="MainLoop"/> that might be waiting on input.</summary>
  105. internal void Wakeup () { MainLoopDriver?.Wakeup (); }
  106. }