diff --git a/Examples/UICatalog/Scenarios/Arrangement.cs b/Examples/UICatalog/Scenarios/Arrangement.cs
index 867e36f1d..7b261db6b 100644
--- a/Examples/UICatalog/Scenarios/Arrangement.cs
+++ b/Examples/UICatalog/Scenarios/Arrangement.cs
@@ -99,7 +99,6 @@ public class Arrangement : Scenario
progressBar.Fraction += 0.01f;
- Application.Wakeup ();
progressBar.SetNeedsDraw ();
};
diff --git a/Examples/UICatalog/Scenarios/Clipping.cs b/Examples/UICatalog/Scenarios/Clipping.cs
index 986a466f2..2cebee301 100644
--- a/Examples/UICatalog/Scenarios/Clipping.cs
+++ b/Examples/UICatalog/Scenarios/Clipping.cs
@@ -100,7 +100,6 @@ public class Clipping : Scenario
{
tiledProgressBar1.Pulse ();
tiledProgressBar2.Pulse ();
- Application.Wakeup ();
};
progressTimer.Start ();
diff --git a/Examples/UICatalog/Scenarios/ProgressBarStyles.cs b/Examples/UICatalog/Scenarios/ProgressBarStyles.cs
index 638303466..a31969c87 100644
--- a/Examples/UICatalog/Scenarios/ProgressBarStyles.cs
+++ b/Examples/UICatalog/Scenarios/ProgressBarStyles.cs
@@ -198,7 +198,6 @@ public class ProgressBarStyles : Scenario
button.Enabled = true;
}
- Application.Wakeup ();
},
null,
0,
@@ -282,7 +281,6 @@ public class ProgressBarStyles : Scenario
marqueesBlocksPB.Text = marqueesContinuousPB.Text = DateTime.Now.TimeOfDay.ToString ();
marqueesBlocksPB.Pulse ();
marqueesContinuousPB.Pulse ();
- Application.Wakeup ();
},
null,
0,
diff --git a/Examples/UICatalog/Scenarios/Shortcuts.cs b/Examples/UICatalog/Scenarios/Shortcuts.cs
index c96dccd7c..fc9843b3c 100644
--- a/Examples/UICatalog/Scenarios/Shortcuts.cs
+++ b/Examples/UICatalog/Scenarios/Shortcuts.cs
@@ -384,7 +384,6 @@ public class Shortcuts : Scenario
pb.Fraction += 0.01f;
- Application.Wakeup ();
pb.SetNeedsDraw ();
}
diff --git a/Terminal.Gui/App/Application.Lifecycle.cs b/Terminal.Gui/App/Application.Lifecycle.cs
index c69ad06f6..4aa915d0f 100644
--- a/Terminal.Gui/App/Application.Lifecycle.cs
+++ b/Terminal.Gui/App/Application.Lifecycle.cs
@@ -131,7 +131,7 @@ public static partial class Application // Lifecycle (Init/Shutdown)
try
{
- MainLoop = Driver!.Init ();
+ Driver!.Init ();
SubscribeDriverEvents ();
}
catch (InvalidOperationException ex)
diff --git a/Terminal.Gui/App/Application.Run.cs b/Terminal.Gui/App/Application.Run.cs
index e136602e6..fc6920ba9 100644
--- a/Terminal.Gui/App/Application.Run.cs
+++ b/Terminal.Gui/App/Application.Run.cs
@@ -372,12 +372,6 @@ public static partial class Application // Run (Begin -> Run -> Layout/Draw -> E
/// the action to be invoked on the main processing thread.
public static void Invoke (Action action) { ApplicationImpl.Instance.Invoke (action); }
- // TODO: Determine if this is really needed. The only code that calls WakeUp I can find
- // is ProgressBarStyles, and it's not clear it needs to.
-
- /// Wakes up the running application that might be waiting on input.
- public static void Wakeup () { MainLoop?.Wakeup (); }
-
///
/// Causes any Toplevels that need layout to be laid out. Then draws any Toplevels that need display. Only Views that
/// need to be laid out (see ) will be laid out.
@@ -396,10 +390,6 @@ public static partial class Application // Run (Begin -> Run -> Layout/Draw -> E
/// See also
public static event EventHandler? Iteration;
- /// The driver for the application
- /// The main loop.
- internal static MainLoop? MainLoop { get; set; }
-
///
/// Set to true to cause to be called after the first iteration. Set to false (the default) to
/// cause the application to continue running until Application.RequestStop () is called.
@@ -417,11 +407,6 @@ public static partial class Application // Run (Begin -> Run -> Layout/Draw -> E
for (state.Toplevel.Running = true; state.Toplevel?.Running == true;)
{
- if (MainLoop is { })
- {
- MainLoop.Running = true;
- }
-
if (EndAfterFirstIteration && !firstIteration)
{
return;
@@ -430,11 +415,6 @@ public static partial class Application // Run (Begin -> Run -> Layout/Draw -> E
firstIteration = RunIteration (ref state, firstIteration);
}
- if (MainLoop is { })
- {
- MainLoop.Running = false;
- }
-
// Run one last iteration to consume any outstanding input events from Driver
// This is important for remaining OnKeyUp events.
RunIteration (ref state, firstIteration);
diff --git a/Terminal.Gui/App/Application.cs b/Terminal.Gui/App/Application.cs
index 718c24772..944c07918 100644
--- a/Terminal.Gui/App/Application.cs
+++ b/Terminal.Gui/App/Application.cs
@@ -216,9 +216,6 @@ public static partial class Application
Top = null;
_cachedRunStateToplevel = null;
- // MainLoop stuff
- MainLoop?.Dispose ();
- MainLoop = null;
MainThreadId = -1;
Iteration = null;
EndAfterFirstIteration = false;
diff --git a/Terminal.Gui/App/MainLoop/IMainLoopDriver.cs b/Terminal.Gui/App/MainLoop/IMainLoopDriver.cs
deleted file mode 100644
index 197535654..000000000
--- a/Terminal.Gui/App/MainLoop/IMainLoopDriver.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-#nullable enable
-namespace Terminal.Gui.App;
-
-/// Interface to create a platform specific driver.
-internal interface IMainLoopDriver
-{
- /// Must report whether there are any events pending, or even block waiting for events.
- /// , if there were pending events, otherwise.
- bool EventsPending ();
-
- /// The iteration function.
- void Iteration ();
-
- /// Initializes the , gets the calling main loop for the initialization.
- /// Call to release resources.
- /// Main loop.
- void Setup (MainLoop mainLoop);
-
- /// Tears down the driver. Releases resources created in .
- void TearDown ();
-
- /// Wakes up the that might be waiting on input, must be thread safe.
- void Wakeup ();
-}
diff --git a/Terminal.Gui/App/MainLoop/LegacyMainLoopDriver.cs b/Terminal.Gui/App/MainLoop/LegacyMainLoopDriver.cs
deleted file mode 100644
index 995baa3b7..000000000
--- a/Terminal.Gui/App/MainLoop/LegacyMainLoopDriver.cs
+++ /dev/null
@@ -1,122 +0,0 @@
-#nullable enable
-//
-// LegacyMainLoopDriver.cs: IMainLoopDriver and MainLoop for legacy v1 driver based applications
-//
-// Authors:
-// Miguel de Icaza (miguel@gnome.org)
-//
-
-using System.Collections.ObjectModel;
-
-namespace Terminal.Gui.App;
-
-///
-/// The main event loop of legacy v1 driver based applications.
-///
-///
-///
-/// This class is provided for backward compatibility with the legacy FakeDriver implementation.
-/// New code should use the modern architecture instead.
-///
-///
-/// Monitoring of file descriptors is only available on Unix, there does not seem to be a way of supporting this
-/// on Windows.
-///
-///
-[Obsolete ("This class is for legacy FakeDriver compatibility only. Use ApplicationMainLoop for new code.")]
-public class MainLoop : IDisposable
-{
- ///
- /// Gets the class responsible for handling timeouts
- ///
- public ITimedEvents TimedEvents { get; } = new TimedEvents();
-
- /// Creates a new MainLoop.
- /// Use to release resources.
- ///
- /// The instance (one of the implementations FakeMainLoop, UnixMainLoop,
- /// NetMainLoop or WindowsMainLoop).
- ///
- internal MainLoop (IMainLoopDriver driver)
- {
- MainLoopDriver = driver;
- driver.Setup (this);
- }
-
-
- /// The current in use.
- /// The main loop driver.
- internal IMainLoopDriver? MainLoopDriver { get; private set; }
-
- /// Used for unit tests.
- internal bool Running { get; set; }
-
-
- ///
- public void Dispose ()
- {
- GC.SuppressFinalize (this);
- Stop ();
- Running = false;
- MainLoopDriver?.TearDown ();
- MainLoopDriver = null;
- }
-
-
- /// Determines whether there are pending events to be processed.
- ///
- /// You can use this method if you want to probe if events are pending. Typically used if you need to flush the
- /// input queue while still running some of your own code in your main thread.
- ///
- internal bool EventsPending () { return MainLoopDriver!.EventsPending (); }
-
-
- /// Runs the . Used only for unit tests.
- internal void Run ()
- {
- bool prev = Running;
- Running = true;
-
- while (Running)
- {
- EventsPending ();
- RunIteration ();
- }
-
- Running = prev;
- }
-
- /// Runs one iteration of timers and file watches
- ///
- /// Use this to process all pending events (timers handlers and file watches).
- ///
- /// while (main.EventsPending ()) RunIteration ();
- ///
- ///
- internal void RunIteration ()
- {
- RunAnsiScheduler ();
-
- MainLoopDriver?.Iteration ();
-
- TimedEvents.RunTimers ();
- }
-
- private void RunAnsiScheduler ()
- {
- Application.Driver?.GetRequestScheduler ().RunSchedule ();
- }
-
- /// Stops the main loop driver and calls . Used only for unit tests.
- internal void Stop ()
- {
- Running = false;
- Wakeup ();
- }
-
-
- /// Wakes up the that might be waiting on input.
- internal void Wakeup () { MainLoopDriver?.Wakeup (); }
-
-
-}
diff --git a/Terminal.Gui/App/MainLoop/MainLoopSyncContext.cs b/Terminal.Gui/App/MainLoop/MainLoopSyncContext.cs
index 2f188b5e0..f67f5ec81 100644
--- a/Terminal.Gui/App/MainLoop/MainLoopSyncContext.cs
+++ b/Terminal.Gui/App/MainLoop/MainLoopSyncContext.cs
@@ -10,23 +10,8 @@ internal sealed class MainLoopSyncContext : SynchronizationContext
public override void Post (SendOrPostCallback d, object state)
{
- // Queue the task
- if (ApplicationImpl.Instance.IsLegacy)
- {
- Application.MainLoop?.TimedEvents.Add (TimeSpan.Zero,
- () =>
- {
- d (state);
-
- return false;
- }
- );
- Application.MainLoop?.Wakeup ();
- }
- else
- {
- ApplicationImpl.Instance.Invoke (() => { d (state); });
- }
+ // Queue the task using the modern architecture
+ ApplicationImpl.Instance.Invoke (() => { d (state); });
}
//_mainLoop.Driver.Wakeup ();
diff --git a/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqUtils.cs b/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqUtils.cs
index 27697bb4b..97dbc933e 100644
--- a/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqUtils.cs
+++ b/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqUtils.cs
@@ -931,7 +931,7 @@ public static class EscSeqUtils
_isButtonClicked = false;
_isButtonDoubleClicked = true;
- Application.MainLoop?.TimedEvents.Add (TimeSpan.Zero,
+ ApplicationImpl.Instance.TimedEvents?.Add (TimeSpan.Zero,
() =>
{
Task.Run (async () => await ProcessButtonDoubleClickedAsync ());
@@ -970,7 +970,7 @@ public static class EscSeqUtils
mouseFlags.Add (GetButtonClicked (buttonState));
_isButtonClicked = true;
- Application.MainLoop?.TimedEvents.Add (TimeSpan.Zero,
+ ApplicationImpl.Instance.TimedEvents?.Add (TimeSpan.Zero,
() =>
{
Task.Run (async () => await ProcessButtonClickedAsync ());
diff --git a/Terminal.Gui/Drivers/ConsoleDriver.cs b/Terminal.Gui/Drivers/ConsoleDriver.cs
index 939000ecc..534adefb7 100644
--- a/Terminal.Gui/Drivers/ConsoleDriver.cs
+++ b/Terminal.Gui/Drivers/ConsoleDriver.cs
@@ -550,8 +550,7 @@ public abstract class ConsoleDriver : IConsoleDriver
#region Setup & Teardown
/// Initializes the driver
- /// Returns an instance of using the for the driver.
- public abstract MainLoop Init ();
+ public abstract void Init ();
/// Ends the execution of the console driver.
public abstract void End ();
diff --git a/Terminal.Gui/Drivers/ConsoleDriverFacade.cs b/Terminal.Gui/Drivers/ConsoleDriverFacade.cs
index d306b06d7..ef12ec04e 100644
--- a/Terminal.Gui/Drivers/ConsoleDriverFacade.cs
+++ b/Terminal.Gui/Drivers/ConsoleDriverFacade.cs
@@ -365,7 +365,7 @@ internal class ConsoleDriverFacade : IConsoleDriver, IConsoleDriverFacade
/// Initializes the driver
/// Returns an instance of using the for the driver.
- public MainLoop Init () { throw new NotSupportedException (); }
+ public void Init () { throw new NotSupportedException (); }
/// Ends the execution of the console driver.
public void End ()
diff --git a/Terminal.Gui/Drivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/Drivers/FakeDriver/FakeDriver.cs
index 5a4e13f50..916760c97 100644
--- a/Terminal.Gui/Drivers/FakeDriver/FakeDriver.cs
+++ b/Terminal.Gui/Drivers/FakeDriver/FakeDriver.cs
@@ -91,9 +91,7 @@ public class FakeDriver : ConsoleDriver
FakeConsole.Clear ();
}
- private FakeMainLoop? _mainLoopDriver;
-
- public override MainLoop Init ()
+ public override void Init ()
{
FakeConsole.MockKeyPresses.Clear ();
@@ -102,12 +100,6 @@ public class FakeDriver : ConsoleDriver
FakeConsole.Clear ();
ResizeScreen ();
CurrentAttribute = new Attribute (Color.White, Color.Black);
- //ClearContents ();
-
- _mainLoopDriver = new FakeMainLoop (this);
- _mainLoopDriver.MockKeyPressed = MockKeyPressedHandler;
-
- return new MainLoop (_mainLoopDriver);
}
public override bool UpdateScreen ()
@@ -346,24 +338,6 @@ public class FakeDriver : ConsoleDriver
private CursorVisibility _savedCursorVisibility;
- private void MockKeyPressedHandler (ConsoleKeyInfo consoleKeyInfo)
- {
- if (consoleKeyInfo.Key == ConsoleKey.Packet)
- {
- consoleKeyInfo = ConsoleKeyMapping.DecodeVKPacketToKConsoleKeyInfo (consoleKeyInfo);
- }
-
- KeyCode map = MapKey (consoleKeyInfo);
-
- if (IsValidInput (map, out map))
- {
- OnKeyDown (new (map));
- OnKeyUp (new (map));
- }
-
- //OnKeyPressed (new KeyEventArgs (map));
- }
-
///
public override bool GetCursorVisibility (out CursorVisibility visibility)
{
diff --git a/Terminal.Gui/Drivers/FakeDriver/FakeMainLoop.cs b/Terminal.Gui/Drivers/FakeDriver/FakeMainLoop.cs
deleted file mode 100644
index d9d918031..000000000
--- a/Terminal.Gui/Drivers/FakeDriver/FakeMainLoop.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-
-namespace Terminal.Gui.Drivers;
-
-internal class FakeMainLoop : IMainLoopDriver
-{
- public Action MockKeyPressed;
-
- public FakeMainLoop (IConsoleDriver consoleDriver = null)
- {
- // No implementation needed for FakeMainLoop
- }
-
- public void Setup (MainLoop mainLoop)
- {
- // No implementation needed for FakeMainLoop
- }
-
- public void Wakeup ()
- {
- // No implementation needed for FakeMainLoop
- }
-
- public bool EventsPending ()
- {
- // Always return true for FakeMainLoop
- return true;
- }
-
- public void Iteration ()
- {
- if (FakeConsole.MockKeyPresses.Count > 0)
- {
- MockKeyPressed?.Invoke (FakeConsole.MockKeyPresses.Pop ());
- }
- }
-
- public void TearDown () { }
-}
diff --git a/Terminal.Gui/Drivers/IConsoleDriver.cs b/Terminal.Gui/Drivers/IConsoleDriver.cs
index 1c42be49b..a28671a4e 100644
--- a/Terminal.Gui/Drivers/IConsoleDriver.cs
+++ b/Terminal.Gui/Drivers/IConsoleDriver.cs
@@ -214,8 +214,7 @@ public interface IConsoleDriver
void UpdateCursor ();
/// Initializes the driver
- /// Returns an instance of using the for the driver.
- MainLoop Init ();
+ void Init ();
/// Ends the execution of the console driver.
void End ();
diff --git a/Tests/UnitTests/Application/ApplicationTests.cs b/Tests/UnitTests/Application/ApplicationTests.cs
index 3816db679..8de8b43b5 100644
--- a/Tests/UnitTests/Application/ApplicationTests.cs
+++ b/Tests/UnitTests/Application/ApplicationTests.cs
@@ -159,7 +159,6 @@ public class ApplicationTests
Application.Shutdown ();
Assert.Null (Application.Top);
- Assert.Null (Application.MainLoop);
Assert.Null (Application.Driver);
}
@@ -316,7 +315,6 @@ public class ApplicationTests
// Don't check Application.Force16Colors
//Assert.False (Application.Force16Colors);
Assert.Null (Application.Driver);
- Assert.Null (Application.MainLoop);
Assert.False (Application.EndAfterFirstIteration);
// Commented out because if CM changed the defaults, those changes should
@@ -472,7 +470,6 @@ public class ApplicationTests
Application.Shutdown ();
Assert.Null (Application.Top);
- Assert.Null (Application.MainLoop);
Assert.Null (Application.Driver);
}
@@ -486,7 +483,6 @@ public class ApplicationTests
Application.Shutdown ();
Assert.Null (Application.Top);
- Assert.Null (Application.MainLoop);
Assert.Null (Application.Driver);
}
@@ -522,14 +518,12 @@ public class ApplicationTests
Application.End (runstate);
Assert.NotNull (Application.Top);
- Assert.NotNull (Application.MainLoop);
Assert.NotNull (Application.Driver);
topLevel.Dispose ();
Application.Shutdown ();
Assert.Null (Application.Top);
- Assert.Null (Application.MainLoop);
Assert.Null (Application.Driver);
}
@@ -676,7 +670,6 @@ public class ApplicationTests
Application.Shutdown ();
Assert.Null (Application.Top);
- Assert.Null (Application.MainLoop);
Assert.Null (Application.Driver);
}
@@ -700,7 +693,6 @@ public class ApplicationTests
Application.Shutdown ();
Assert.Null (Application.Top);
- Assert.Null (Application.MainLoop);
Assert.Null (Application.Driver);
}
@@ -734,7 +726,6 @@ public class ApplicationTests
Application.Shutdown ();
Assert.Null (Application.Top);
- Assert.Null (Application.MainLoop);
Assert.Null (Application.Driver);
}
@@ -752,7 +743,6 @@ public class ApplicationTests
Application.Shutdown ();
Assert.Null (Application.Top);
- Assert.Null (Application.MainLoop);
Assert.Null (Application.Driver);
}
@@ -774,7 +764,6 @@ public class ApplicationTests
Application.Shutdown ();
Assert.Null (Application.Top);
- Assert.Null (Application.MainLoop);
Assert.Null (Application.Driver);
a.After (null);
@@ -793,7 +782,6 @@ public class ApplicationTests
Application.Shutdown ();
Assert.Null (Application.Top);
- Assert.Null (Application.MainLoop);
Assert.Null (Application.Driver);
}
@@ -812,7 +800,6 @@ public class ApplicationTests
Application.Shutdown ();
Assert.Null (Application.Top);
- Assert.Null (Application.MainLoop);
Assert.Null (Application.Driver);
}
@@ -829,7 +816,6 @@ public class ApplicationTests
Application.Shutdown ();
Assert.Null (Application.Top);
- Assert.Null (Application.MainLoop);
Assert.Null (Application.Driver);
}
@@ -849,7 +835,6 @@ public class ApplicationTests
top.Dispose ();
Application.Shutdown ();
Assert.Null (Application.Top);
- Assert.Null (Application.MainLoop);
Assert.Null (Application.Driver);
}
@@ -872,7 +857,6 @@ public class ApplicationTests
top.Dispose ();
Application.Shutdown ();
Assert.Null (Application.Top);
- Assert.Null (Application.MainLoop);
Assert.Null (Application.Driver);
}
@@ -892,7 +876,6 @@ public class ApplicationTests
top.Dispose ();
Application.Shutdown ();
Assert.Null (Application.Top);
- Assert.Null (Application.MainLoop);
Assert.Null (Application.Driver);
}
diff --git a/Tests/UnitTests/Application/MainLoopTests.cs b/Tests/UnitTests/Application/MainLoopTests.cs
deleted file mode 100644
index 0a04f3c4f..000000000
--- a/Tests/UnitTests/Application/MainLoopTests.cs
+++ /dev/null
@@ -1,940 +0,0 @@
-using System.Diagnostics;
-using Xunit.Abstractions;
-
-// Alias Console to MockConsole so we don't accidentally use Console
-
-namespace UnitTests.ApplicationTests;
-
-/// Tests MainLoop using the FakeMainLoop.
-public class MainLoopTests
-{
- private readonly ITestOutputHelper _output;
-
- public MainLoopTests (ITestOutputHelper output)
- {
- _output = output;
- ConsoleDriver.RunningUnitTests = true;
- }
-
- private static Button btn;
- private static string cancel;
- private static string clickMe;
- private static int four;
- private static int one;
- private static string pewPew;
- private static bool taskCompleted;
-
- // TODO: EventsPending tests
- // - wait = true
- // - wait = false
-
- // TODO: Add IMainLoop tests
- private static int three;
- private static int total;
- private static int two;
- private static int zero;
-
- // See Also ConsoleDRivers/MainLoopDriverTests.cs for tests of the MainLoopDriver
-
- // Idle Handler tests
- [Fact]
- public void AddTimeout_Adds_And_Removes ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
-
- Func fnTrue = () => true;
- Func fnFalse = () => false;
-
- var a = ml.TimedEvents.Add (TimeSpan.Zero, fnTrue);
- var b = ml.TimedEvents.Add (TimeSpan.Zero, fnFalse);
-
- Assert.Equal (2, ml.TimedEvents.Timeouts.Count);
- Assert.Equal (fnTrue, ml.TimedEvents.Timeouts.ElementAt (0).Value.Callback);
- Assert.NotEqual (fnFalse, ml.TimedEvents.Timeouts.ElementAt (0).Value.Callback);
-
- Assert.True (ml.TimedEvents.Remove (a));
- Assert.Single (ml.TimedEvents.Timeouts);
-
- // BUGBUG: This doesn't throw or indicate an error. Ideally RemoveIdle would either
- // throw an exception in this case, or return an error.
- // No. Only need to return a boolean.
- Assert.False (ml.TimedEvents.Remove (a));
-
- Assert.True (ml.TimedEvents.Remove (b));
-
- // BUGBUG: This doesn't throw an exception or indicate an error. Ideally RemoveIdle would either
- // throw an exception in this case, or return an error.
- // No. Only need to return a boolean.
- Assert.False (ml.TimedEvents.Remove(b));
- }
-
- [Fact]
- public void AddTimeout_Function_GetsCalled_OnIteration ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
-
- var functionCalled = 0;
-
- Func fn = () =>
- {
- functionCalled++;
-
- return true;
- };
-
- ml.TimedEvents.Add (TimeSpan.Zero, fn);
- ml.RunIteration ();
- Assert.Equal (1, functionCalled);
- }
-
- [Fact]
- public void AddTimeout_Twice_Returns_False_Called_Twice ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
-
- var functionCalled = 0;
-
- Func fn1 = () =>
- {
- functionCalled++;
-
- return false;
- };
-
- // Force stop if 10 iterations
- var stopCount = 0;
-
- Func fnStop = () =>
- {
- stopCount++;
-
- if (stopCount == 10)
- {
- ml.Stop ();
- }
-
- return true;
- };
-
- var a = ml.TimedEvents.Add (TimeSpan.Zero, fnStop);
- var b = ml.TimedEvents.Add (TimeSpan.Zero, fn1);
- ml.Run ();
-
- Assert.True (ml.TimedEvents.Remove(a));
- Assert.False (ml.TimedEvents.Remove (a));
-
- // Cannot remove b because it returned false i.e. auto removes itself
- Assert.False (ml.TimedEvents.Remove (b));
-
- Assert.Equal (1, functionCalled);
- }
-
- [Fact]
- public void AddTimeoutTwice_Function_CalledTwice ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
-
- var functionCalled = 0;
-
- Func fn = () =>
- {
- functionCalled++;
-
- return true;
- };
-
- var a = ml.TimedEvents.Add (TimeSpan.Zero, fn);
- var b = ml.TimedEvents.Add (TimeSpan.Zero, fn);
- ml.RunIteration ();
- Assert.Equal (2, functionCalled);
- Assert.Equal (2, ml.TimedEvents.Timeouts.Count);
-
- functionCalled = 0;
- Assert.True (ml.TimedEvents.Remove (a));
- Assert.Single (ml.TimedEvents.Timeouts);
- ml.RunIteration ();
- Assert.Equal (1, functionCalled);
-
- functionCalled = 0;
- Assert.True (ml.TimedEvents.Remove (b));
- Assert.Empty (ml.TimedEvents.Timeouts);
- ml.RunIteration ();
- Assert.Equal (0, functionCalled);
- Assert.False (ml.TimedEvents.Remove (b));
- }
-
- [Fact]
- public void AddThenRemoveIdle_Function_NotCalled ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
-
- var functionCalled = 0;
-
- Func fn = () =>
- {
- functionCalled++;
-
- return true;
- };
-
- var a = ml.TimedEvents.Add (TimeSpan.Zero, fn);
- Assert.True (ml.TimedEvents.Remove (a));
- ml.RunIteration ();
- Assert.Equal (0, functionCalled);
- }
-
- // Timeout Handler Tests
- [Fact]
- public void AddTimer_Adds_Removes_NoFaults ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
- var ms = 100;
-
- var callbackCount = 0;
-
- Func callback = () =>
- {
- callbackCount++;
-
- return true;
- };
-
- object token = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback);
-
- Assert.True (ml.TimedEvents.Remove (token));
-
- // BUGBUG: This should probably fault?
- // Must return a boolean.
- Assert.False (ml.TimedEvents.Remove (token));
- }
-
- [Fact]
- public async Task AddTimer_Duplicate_Keys_Not_Allowed ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
- const int ms = 100;
- object token1 = null, token2 = null;
-
- var callbackCount = 0;
-
- Func callback = () =>
- {
- callbackCount++;
-
- if (callbackCount == 2)
- {
- ml.Stop ();
- }
-
- return true;
- };
-
- var task1 = new Task (() => token1 = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback));
- var task2 = new Task (() => token2 = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback));
- Assert.Null (token1);
- Assert.Null (token2);
- task1.Start ();
- task2.Start ();
- ml.Run ();
- Assert.NotNull (token1);
- Assert.NotNull (token2);
- await Task.WhenAll (task1, task2);
- Assert.True (ml.TimedEvents.Remove (token1));
- Assert.True (ml.TimedEvents.Remove (token2));
-
- Assert.Equal (2, callbackCount);
- }
-
- // Timeout Handler Tests
- [Fact]
- public void AddTimer_EventFired ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
- var ms = 100;
-
- // Use Stopwatch ticks since TimedEvents now uses Stopwatch.GetTimestamp internally
- long originTicks = Stopwatch.GetTimestamp () * TimeSpan.TicksPerSecond / Stopwatch.Frequency;
-
- var callbackCount = 0;
-
- Func callback = () =>
- {
- callbackCount++;
-
- return true;
- };
-
- object sender = null;
- TimeoutEventArgs args = null;
-
- ml.TimedEvents.Added += (s, e) =>
- {
- sender = s;
- args = e;
- };
-
- object token = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback);
-
- Assert.Same (ml.TimedEvents, sender);
- Assert.NotNull (args.Timeout);
- Assert.True (args.Ticks - originTicks >= 100 * TimeSpan.TicksPerMillisecond);
- }
-
- [Fact]
- public void AddTimer_In_Parallel_Wont_Throw ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
- const int ms = 100;
- object token1 = null, token2 = null;
-
- var callbackCount = 0;
-
- Func callback = () =>
- {
- callbackCount++;
-
- if (callbackCount == 2)
- {
- ml.Stop ();
- }
-
- return true;
- };
-
- Parallel.Invoke (
- () => token1 = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback),
- () => token2 = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback)
- );
- ml.Run ();
- Assert.NotNull (token1);
- Assert.NotNull (token2);
- Assert.True (ml.TimedEvents.Remove (token1));
- Assert.True (ml.TimedEvents.Remove (token2));
-
- Assert.Equal (2, callbackCount);
- }
-
- [Fact]
- public void AddTimer_Remove_NotCalled ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
- TimeSpan ms = TimeSpan.FromMilliseconds (50);
-
- // Force stop if 10 iterations
- var stopCount = 0;
-
- Func fnStop = () =>
- {
- stopCount++;
-
- if (stopCount == 10)
- {
- ml.Stop ();
- }
-
- return true;
- };
- ml.TimedEvents.Add (TimeSpan.Zero, fnStop);
-
- var callbackCount = 0;
-
- Func callback = () =>
- {
- callbackCount++;
-
- return true;
- };
-
- object token = ml.TimedEvents.Add (ms, callback);
- Assert.True (ml.TimedEvents.Remove (token));
- ml.Run ();
- Assert.Equal (0, callbackCount);
- }
-
- [Fact]
- public void AddTimer_ReturnFalse_StopsBeingCalled ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
- TimeSpan ms = TimeSpan.FromMilliseconds (50);
-
- // Force stop if 10 iterations
- var stopCount = 0;
-
- Func fnStop = () =>
- {
- Thread.Sleep (10); // Sleep to enable timer to fire
- stopCount++;
-
- if (stopCount == 10)
- {
- ml.Stop ();
- }
-
- return true;
- };
- ml.TimedEvents.Add (TimeSpan.Zero, fnStop);
-
- var callbackCount = 0;
-
- Func callback = () =>
- {
- callbackCount++;
-
- return false;
- };
-
- object token = ml.TimedEvents.Add (ms, callback);
- ml.Run ();
- Assert.Equal (1, callbackCount);
- Assert.Equal (10, stopCount);
- Assert.False (ml.TimedEvents.Remove (token));
- }
-
- [Fact]
- public void AddTimer_Run_Called ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
- var ms = 100;
-
- var callbackCount = 0;
-
- Func callback = () =>
- {
- callbackCount++;
- ml.Stop ();
-
- return true;
- };
-
- object token = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback);
- ml.Run ();
- Assert.True (ml.TimedEvents.Remove (token));
-
- Assert.Equal (1, callbackCount);
- }
-
- [Fact]
- public void AddTimer_Run_CalledAtApproximatelyRightTime ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
- TimeSpan ms = TimeSpan.FromMilliseconds (50);
- var watch = new Stopwatch ();
-
- var callbackCount = 0;
-
- Func callback = () =>
- {
- watch.Stop ();
- callbackCount++;
- ml.Stop ();
-
- return true;
- };
-
- object token = ml.TimedEvents.Add (ms, callback);
- watch.Start ();
- ml.Run ();
-
- // +/- 100ms should be good enuf
- // https://github.com/xunit/assert.xunit/pull/25
- Assert.Equal (ms * callbackCount, watch.Elapsed, new MillisecondTolerance (100));
-
- Assert.True (ml.TimedEvents.Remove (token));
- Assert.Equal (1, callbackCount);
- }
-
- [Fact]
- public void AddTimer_Run_CalledTwiceApproximatelyRightTime ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
- TimeSpan ms = TimeSpan.FromMilliseconds (50);
- var watch = new Stopwatch ();
-
- var callbackCount = 0;
-
- Func callback = () =>
- {
- callbackCount++;
-
- if (callbackCount == 2)
- {
- watch.Stop ();
- ml.Stop ();
- }
-
- return true;
- };
-
- object token = ml.TimedEvents.Add (ms, callback);
- watch.Start ();
- ml.Run ();
-
- // +/- 100ms should be good enuf
- // https://github.com/xunit/assert.xunit/pull/25
- Assert.Equal (ms * callbackCount, watch.Elapsed, new MillisecondTolerance (100));
-
- Assert.True (ml.TimedEvents.Remove (token));
- Assert.Equal (2, callbackCount);
- }
-
- [Fact]
- public void CheckTimersAndIdleHandlers_NoTimers_Returns_False ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
- bool retVal = ml.TimedEvents.CheckTimers(out int waitTimeOut);
- Assert.False (retVal);
- Assert.Equal (-1, waitTimeOut);
- }
-
- [Fact]
- public void CheckTimersAndIdleHandlers_NoTimers_WithIdle_Returns_True ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
- Func fnTrue = () => true;
-
- ml.TimedEvents.Add (TimeSpan.Zero, fnTrue);
- bool retVal = ml.TimedEvents.CheckTimers(out int waitTimeOut);
- Assert.True (retVal);
- Assert.Equal (0, waitTimeOut);
- }
-
- [Fact]
- public void CheckTimersAndIdleHandlers_With1Timer_Returns_Timer ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
- TimeSpan ms = TimeSpan.FromMilliseconds (50);
-
- static bool Callback () { return false; }
-
- _ = ml.TimedEvents.Add (ms, Callback);
- bool retVal = ml.TimedEvents.CheckTimers (out int waitTimeOut);
-
- Assert.True (retVal);
-
- // It should take < 10ms to execute to here
- Assert.True (ms.TotalMilliseconds <= waitTimeOut + 10);
- }
-
- [Fact]
- public void CheckTimersAndIdleHandlers_With2Timers_Returns_Timer ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
- TimeSpan ms = TimeSpan.FromMilliseconds (50);
-
- static bool Callback () { return false; }
-
- _ = ml.TimedEvents.Add (ms, Callback);
- _ = ml.TimedEvents.Add (ms, Callback);
- bool retVal = ml.TimedEvents.CheckTimers (out int waitTimeOut);
-
- Assert.True (retVal);
-
- // It should take < 10ms to execute to here
- Assert.True (ms.TotalMilliseconds <= waitTimeOut + 10);
- }
-
- [Fact]
- public void False_Idle_Stops_It_Being_Called_Again ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
-
- var functionCalled = 0;
-
- Func fn1 = () =>
- {
- functionCalled++;
-
- if (functionCalled == 10)
- {
- return false;
- }
-
- return true;
- };
-
- // Force stop if 20 iterations
- var stopCount = 0;
-
- Func fnStop = () =>
- {
- stopCount++;
-
- if (stopCount == 20)
- {
- ml.Stop ();
- }
-
- return true;
- };
-
- var a = ml.TimedEvents.Add (TimeSpan.Zero, fnStop);
- var b = ml.TimedEvents.Add (TimeSpan.Zero, fn1);
- ml.Run ();
- Assert.True (ml.TimedEvents.Remove (a));
- Assert.False (ml.TimedEvents.Remove (a));
-
- Assert.Equal (10, functionCalled);
- Assert.Equal (20, stopCount);
- }
-
- [Fact]
- public void Internal_Tests ()
- {
- var testMainloop = new TestMainloop ();
- var mainloop = new MainLoop (testMainloop);
- Assert.Empty (mainloop.TimedEvents.Timeouts);
-
- Assert.NotNull (
- new Terminal.Gui.App.Timeout { Span = new (), Callback = () => true }
- );
- }
-
- [Theory]
- [MemberData (nameof (TestAddTimeout))]
- public void Mainloop_Invoke_Or_AddTimeout_Can_Be_Used_For_Events_Or_Actions (
- Action action,
- string pclickMe,
- string pcancel,
- string ppewPew,
- int pzero,
- int pone,
- int ptwo,
- int pthree,
- int pfour
- )
- {
- // TODO: Expand this test to test all drivers
- Application.Init (null, "fakedriver");
-
- total = 0;
- btn = null;
- clickMe = pclickMe;
- cancel = pcancel;
- pewPew = ppewPew;
- zero = pzero;
- one = pone;
- two = ptwo;
- three = pthree;
- four = pfour;
- taskCompleted = false;
-
- var btnLaunch = new Button { Text = "Open Window" };
-
- btnLaunch.Accepting += (s, e) => action ();
-
- var top = new Toplevel ();
- top.Add (btnLaunch);
-
- int iterations = -1;
-
- Application.Iteration += (s, a) =>
- {
- iterations++;
-
- if (iterations == 0)
- {
- Assert.Null (btn);
- Assert.Equal (zero, total);
- Assert.False (btnLaunch.NewKeyDownEvent (Key.Space));
-
- if (btn == null)
- {
- Assert.Null (btn);
- Assert.Equal (zero, total);
- }
- else
- {
- Assert.Equal (clickMe, btn.Text);
- Assert.Equal (four, total);
- }
- }
- else if (iterations == 1)
- {
- Assert.Equal (clickMe, btn.Text);
- Assert.Equal (zero, total);
- Assert.False (btn.NewKeyDownEvent (Key.Space));
- Assert.Equal (cancel, btn.Text);
- Assert.Equal (one, total);
- }
- else if (taskCompleted)
- {
- Application.RequestStop ();
- }
- };
-
- Application.Run (top);
- top.Dispose ();
-
- Assert.True (taskCompleted);
- Assert.Equal (clickMe, btn.Text);
- Assert.Equal (four, total);
-
- Application.Shutdown ();
- }
-
- [Fact]
- public void RemoveIdle_Function_NotCalled ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
-
- var functionCalled = 0;
-
- Func fn = () =>
- {
- functionCalled++;
-
- return true;
- };
-
- Assert.False (ml.TimedEvents.Remove ("flibble"));
- ml.RunIteration ();
- Assert.Equal (0, functionCalled);
- }
-
- [Fact]
- public void Run_Runs_Idle_Stop_Stops_Idle ()
- {
- var ml = new MainLoop (new FakeMainLoop ());
-
- var functionCalled = 0;
-
- Func fn = () =>
- {
- functionCalled++;
-
- if (functionCalled == 10)
- {
- ml.Stop ();
- }
-
- return true;
- };
-
- var a = ml.TimedEvents.Add (TimeSpan.Zero, fn);
- ml.Run ();
- Assert.True (ml.TimedEvents.Remove (a));
-
- Assert.Equal (10, functionCalled);
- }
-
- [Theory]
- [InlineData ("fake")]
- [InlineData ("windows")]
- [InlineData ("dotnet")]
- [InlineData ("unix")]
- public void Application_Invoke_Run_TimedEvents (string driverName)
- {
- // Arrange
- Application.Init (driverName: driverName);
- var functionCalled = 0;
- var stopwatch = new Stopwatch ();
-
- // Act
- Application.Invoke (() =>
- {
- // Stop the stopwatch *after* the function is called.
- functionCalled++;
- stopwatch.Stop ();
- Application.RequestStop ();
- });
-
- // Start timing just before running the application loop.
- stopwatch.Start ();
- Application.Run ();
-
- // Assert
- Assert.NotNull (Application.Top);
- Application.Top.Dispose ();
- Application.Shutdown ();
- Assert.Equal (1, functionCalled);
-
- // Output the elapsed time for this test case.
- // ReSharper disable once Xunit.XunitTestWithConsoleOutput
- // ReSharper disable once LocalizableElement
- Console.WriteLine ($"[{driverName}] Duration: {stopwatch.Elapsed.TotalMilliseconds:F2} ms");
-
- // Output elapsed duration to xUnit's test output
- _output.WriteLine ($"[{driverName}] Duration: {stopwatch.Elapsed.TotalMilliseconds:F2} ms");
- }
-
- [Theory]
- [InlineData ("fake")]
- [InlineData ("windows")]
- [InlineData ("dotnet")]
- [InlineData ("unix")]
- public void Application_AddTimeout_Run_TimedEvents (string driverName)
- {
- // Arrange
- Application.Init (driverName: driverName);
- var functionCalled = 0;
- var stopwatch = new Stopwatch ();
-
- // Act
- bool Function ()
- {
- functionCalled++;
-
- if (functionCalled == 10 && Application.Top is { Running: true })
- {
- stopwatch.Stop ();
- Application.RequestStop ();
-
- return false;
- }
-
- return true;
- }
-
- Application.AddTimeout (TimeSpan.FromMilliseconds (1), Function);
-
- // Start timing just before running the application loop.
- stopwatch.Start ();
- Application.Run ();
-
- // Assert
- Assert.NotNull (Application.Top);
- Application.Top.Dispose ();
- Application.Shutdown ();
- Assert.Equal (10, functionCalled);
-
- // Output the elapsed time for this test case.
- // ReSharper disable once Xunit.XunitTestWithConsoleOutput
- // ReSharper disable once LocalizableElement
- Console.WriteLine ($"[{driverName}] Duration: {stopwatch.Elapsed.TotalMilliseconds:F2} ms");
-
- // Output elapsed duration to xUnit's test output
- _output.WriteLine ($"[{driverName}] Duration: {stopwatch.Elapsed.TotalMilliseconds:F2} ms");
- }
-
- public static IEnumerable