浏览代码

Fixes #2921 - MainLoop refactoring (#2922)

* Adds basic MainLoop unit tests

* Remove WinChange action from Curses

* Remove WinChange action from Curses

* Remove ProcessInput action from Windows MainLoop

* Simplified MainLoop/ConsoleDriver by making MainLoop internal and moving impt fns to Application

* Modernized Terminal resize events

* Modernized Terminal resize events

* Removed un used property

* for _isWindowsTerminal devenv->wininit; not sure what changed

* Modernized mouse/keyboard events (Action->EventHandler)

* Updated OnMouseEvent API docs

* Using WT_SESSION to detect WT

* removes hacky GetParentProcess

* Updates to fix #2634 (clear last line)

* removes hacky GetParentProcess2

* Addressed mac resize issue

* Addressed mac resize issue

* Removes ConsoleDriver.PrepareToRun, has Init return MainLoop

* Removes unneeded Attribute methods

* Removed GetProcesssName

* Removed GetProcesssName

* Refactored KeyEvent and KeyEventEventArgs into a single class

* Revert "Refactored KeyEvent and KeyEventEventArgs into a single class"

This reverts commit 88a00658dbcb53306d56af1b766594c0eea10b2c.

* Fixed key repeat issue; reverted stupidity on 1049/1047 confusion

* Updated CSI API Docs

* merge
Tig 1 年之前
父节点
当前提交
6851b42a49
共有 94 个文件被更改,包括 3027 次插入2946 次删除
  1. 3 3
      CONTRIBUTING.md
  2. 3 3
      ReactiveExample/TerminalScheduler.cs
  3. 271 172
      Terminal.Gui/Application.cs
  4. 1 1
      Terminal.Gui/Configuration/ConfigurationManager.cs
  5. 81 43
      Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs
  6. 111 152
      Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
  7. 7 5
      Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs
  8. 23 2
      Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs
  9. 14 22
      Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
  10. 34 50
      Terminal.Gui/ConsoleDrivers/NetDriver.cs
  11. 79 157
      Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
  12. 2 1
      Terminal.Gui/Drawing/Color.cs
  13. 3 1
      Terminal.Gui/Input/Event.cs
  14. 2 0
      Terminal.Gui/Input/MouseEventEventArgs.cs
  15. 40 43
      Terminal.Gui/MainLoop.cs
  16. 1 1
      Terminal.Gui/Timeout.cs
  17. 1 1
      Terminal.Gui/TimeoutEventArgs.cs
  18. 0 32
      Terminal.Gui/View/Layout/ResizedEventArgs.cs
  19. 5 0
      Terminal.Gui/View/Layout/SizeChangedEventArgs.cs
  20. 6 6
      Terminal.Gui/View/ViewKeyboard.cs
  21. 1 1
      Terminal.Gui/View/ViewLayout.cs
  22. 4 4
      Terminal.Gui/Views/Button.cs
  23. 7 7
      Terminal.Gui/Views/FileDialog.cs
  24. 1 1
      Terminal.Gui/Views/Label.cs
  25. 12 12
      Terminal.Gui/Views/Menu.cs
  26. 1 0
      Terminal.Gui/Views/Slider.cs
  27. 5 5
      Terminal.Gui/Views/SpinnerView/SpinnerView.cs
  28. 2 2
      Terminal.Gui/Views/TableView/TreeTableSource.cs
  29. 8 22
      Terminal.Gui/Views/Toplevel.cs
  30. 20 20
      Terminal.Gui/Views/ToplevelOverlapped.cs
  31. 1 0
      Terminal.sln
  32. 2 2
      UICatalog/KeyBindingsDialog.cs
  33. 1 1
      UICatalog/Properties/launchSettings.json
  34. 2 2
      UICatalog/Scenarios/ASCIICustomButton.cs
  35. 1 1
      UICatalog/Scenarios/Animation.cs
  36. 2 2
      UICatalog/Scenarios/BackgroundWorkerCollection.cs
  37. 4 4
      UICatalog/Scenarios/BasicColors.cs
  38. 1 1
      UICatalog/Scenarios/CharacterMap.cs
  39. 5 5
      UICatalog/Scenarios/ContextMenus.cs
  40. 1 1
      UICatalog/Scenarios/CsvEditor.cs
  41. 1 1
      UICatalog/Scenarios/Editor.cs
  42. 2 2
      UICatalog/Scenarios/GraphViewExample.cs
  43. 1 1
      UICatalog/Scenarios/InteractiveTree.cs
  44. 19 18
      UICatalog/Scenarios/Keys.cs
  45. 1 1
      UICatalog/Scenarios/LineDrawing.cs
  46. 1 1
      UICatalog/Scenarios/ListColumns.cs
  47. 3 3
      UICatalog/Scenarios/Mouse.cs
  48. 2 2
      UICatalog/Scenarios/ProcessTable.cs
  49. 6 6
      UICatalog/Scenarios/Progress.cs
  50. 2 2
      UICatalog/Scenarios/ProgressBarStyles.cs
  51. 4 4
      UICatalog/Scenarios/Scrolling.cs
  52. 2 2
      UICatalog/Scenarios/SendKeys.cs
  53. 1 1
      UICatalog/Scenarios/SingleBackgroundWorker.cs
  54. 1 1
      UICatalog/Scenarios/Snake.cs
  55. 1 1
      UICatalog/Scenarios/TableEditor.cs
  56. 1 1
      UICatalog/Scenarios/Threading.cs
  57. 2 2
      UICatalog/Scenarios/TreeViewFileSystem.cs
  58. 3 3
      UICatalog/Scenarios/TrueColors.cs
  59. 5 5
      UICatalog/Scenarios/VkeyPacketSimulator.cs
  60. 859 859
      UnitTests/Application/ApplicationTests.cs
  61. 698 718
      UnitTests/Application/MainLoopTests.cs
  62. 2 2
      UnitTests/Application/SynchronizatonContextTests.cs
  63. 8 8
      UnitTests/Clipboard/ClipboardTests.cs
  64. 0 3
      UnitTests/Configuration/ConfigurationMangerTests.cs
  65. 5 5
      UnitTests/ConsoleDrivers/AddRuneTests.cs
  66. 13 13
      UnitTests/ConsoleDrivers/ConsoleDriverTests.cs
  67. 2 2
      UnitTests/ConsoleDrivers/DriverColorTests.cs
  68. 2 2
      UnitTests/ConsoleDrivers/KeyTests.cs
  69. 270 0
      UnitTests/ConsoleDrivers/MainLoopDriverTests.cs
  70. 13 13
      UnitTests/Dialogs/DialogTests.cs
  71. 14 14
      UnitTests/Dialogs/MessageBoxTests.cs
  72. 5 5
      UnitTests/Dialogs/WizardTests.cs
  73. 3 3
      UnitTests/Drawing/AttributeTests.cs
  74. 11 17
      UnitTests/Input/EscSeqUtilsTests.cs
  75. 0 35
      UnitTests/ReflectionTools.cs
  76. 8 8
      UnitTests/UICatalog/ScenarioTests.cs
  77. 8 8
      UnitTests/View/FrameTests.cs
  78. 6 6
      UnitTests/View/KeyboardTests.cs
  79. 6 6
      UnitTests/View/Layout/DimTests.cs
  80. 15 18
      UnitTests/View/Layout/LayoutTests.cs
  81. 7 7
      UnitTests/View/Layout/PosTests.cs
  82. 11 11
      UnitTests/View/NavigationTests.cs
  83. 4 4
      UnitTests/View/ViewTests.cs
  84. 32 47
      UnitTests/Views/ContextMenuTests.cs
  85. 2 2
      UnitTests/Views/GraphViewTests.cs
  86. 32 44
      UnitTests/Views/MenuTests.cs
  87. 10 10
      UnitTests/Views/OverlappedTests.cs
  88. 10 16
      UnitTests/Views/ScrollBarViewTests.cs
  89. 2 2
      UnitTests/Views/StatusBarTests.cs
  90. 1 1
      UnitTests/Views/TabViewTests.cs
  91. 13 17
      UnitTests/Views/TextFieldTests.cs
  92. 9 9
      UnitTests/Views/TextViewTests.cs
  93. 119 185
      UnitTests/Views/ToplevelTests.cs
  94. 1 1
      UnitTests/Views/TreeViewTests.cs

+ 3 - 3
CONTRIBUTING.md

@@ -148,11 +148,11 @@ The [Microsoft .NET Framework Design Guidelines](https://docs.microsoft.com/en-u
 > ✔️ DO name event argument classes with the "EventArgs" suffix.
 
 1. We follow the naming guidelines provided in https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/names-of-type-members?redirectedfrom=MSDN
-2. We use the `event Action<T>` idiom.
+2. We use the `event EventHandler<T>` idiom.
 3. For public APIs, the class that can raise the event will implement:
    - A `virtual` event raising function, named as `OnEventToRaise`. Typical implementations will simply do a `EventToRaise?.Invoke(this, eventArgs)`.
-   - An `event` as in `public event Action<EventArgs> EventToRaise`
-   - Consumers of the event can do `theobject.EventToRaise += (args) => {};`
+   - An `event` as in `public event EventHandler<EventArgs> EventToRaise`
+   - Consumers of the event can do `theobject.EventToRaise += (sender, args) => {};`
    - Sub-classes of the class implementing `EventToRaise` can override `OnEventToRaise` as needed.
 4. Where possible, a subclass of `EventArgs` should be provided and the old and new state should be included. By doing this, event handler methods do not have to query the sender for state.
 

+ 3 - 3
ReactiveExample/TerminalScheduler.cs

@@ -15,7 +15,7 @@ namespace ReactiveExample {
 			IDisposable PostOnMainLoop() {
 				var composite = new CompositeDisposable(2);
 				var cancellation = new CancellationDisposable();
-				Application.MainLoop.Invoke (() => {
+				Application.Invoke (() => {
 					if (!cancellation.Token.IsCancellationRequested)
 						composite.Add(action(this, state));
 				});
@@ -25,11 +25,11 @@ namespace ReactiveExample {
 
 			IDisposable PostOnMainLoopAsTimeout () {
 				var composite = new CompositeDisposable (2);
-				var timeout = Application.MainLoop.AddTimeout (dueTime, args => {
+				var timeout = Application.AddTimeout (dueTime, () => {
 					composite.Add(action (this, state));
 					return false;
 				});
-				composite.Add (Disposable.Create (() => Application.MainLoop.RemoveTimeout (timeout)));
+				composite.Add (Disposable.Create (() => Application.RemoveTimeout (timeout)));
 				return composite;
 			}
 

+ 271 - 172
Terminal.Gui/Application.cs

@@ -108,19 +108,15 @@ namespace Terminal.Gui {
 		/// returned) to ensure resources are cleaned up and terminal settings restored.
 		/// </para>
 		/// <para>
-		/// The <see cref="Run{T}(Func{Exception, bool}, ConsoleDriver, IMainLoopDriver)"/> function 
-		/// combines <see cref="Init(ConsoleDriver, IMainLoopDriver)"/> and <see cref="Run(Toplevel, Func{Exception, bool})"/>
-		/// into a single call. An application cam use <see cref="Run{T}(Func{Exception, bool}, ConsoleDriver, IMainLoopDriver)"/> 
-		/// without explicitly calling <see cref="Init(ConsoleDriver, IMainLoopDriver)"/>.
+		/// The <see cref="Run{T}(Func{Exception, bool}, ConsoleDriver)"/> function 
+		/// combines <see cref="Init(ConsoleDriver)"/> and <see cref="Run(Toplevel, Func{Exception, bool})"/>
+		/// into a single call. An application cam use <see cref="Run{T}(Func{Exception, bool}, ConsoleDriver)"/> 
+		/// without explicitly calling <see cref="Init(ConsoleDriver)"/>.
 		/// </para>
 		/// <param name="driver">
 		/// The <see cref="ConsoleDriver"/> to use. If not specified the default driver for the
 		/// platform will be used (see <see cref="WindowsDriver"/>, <see cref="CursesDriver"/>, and <see cref="NetDriver"/>).</param>
-		/// <param name="mainLoopDriver">
-		/// Specifies the <see cref="MainLoop"/> to use. 
-		/// Must not be <see langword="null"/> if <paramref name="driver"/> is not <see langword="null"/>.
-		/// </param>
-		public static void Init (ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null) => InternalInit (() => Toplevel.Create (), driver, mainLoopDriver);
+		public static void Init (ConsoleDriver driver = null) => InternalInit (() => Toplevel.Create (), driver);
 
 		internal static bool _initialized = false;
 		internal static int _mainThreadId = -1;
@@ -134,7 +130,7 @@ namespace Terminal.Gui {
 		// Unit Tests - To initialize the app with a custom Toplevel, using the FakeDriver. calledViaRunT will be false, causing all state to be reset.
 		// 
 		// calledViaRunT: If false (default) all state will be reset. If true the state will not be reset.
-		internal static void InternalInit (Func<Toplevel> topLevelFactory, ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null, bool calledViaRunT = false)
+		internal static void InternalInit (Func<Toplevel> topLevelFactory, ConsoleDriver driver = null, bool calledViaRunT = false)
 		{
 			if (_initialized && driver == null) {
 				return;
@@ -179,26 +175,8 @@ namespace Terminal.Gui {
 				}
 			}
 
-			if (mainLoopDriver == null) {
-				// TODO: Move this logic into ConsoleDriver
-				if (Driver is FakeDriver) {
-					mainLoopDriver = new FakeMainLoop (Driver);
-				} else if (Driver is NetDriver) {
-					mainLoopDriver = new NetMainLoop (Driver);
-				} else if (Driver is WindowsDriver) {
-					mainLoopDriver = new WindowsMainLoop (Driver);
-				} else if (Driver is CursesDriver) {
-					mainLoopDriver = new UnixMainLoop (Driver);
-				}
-				if (mainLoopDriver == null) {
-					throw new InvalidOperationException ("Init could not determine the MainLoopDriver to use.");
-				}
-			}
-
-			MainLoop = new MainLoop (mainLoopDriver);
-
 			try {
-				Driver.Init (OnTerminalResized);
+				MainLoop = Driver.Init ();
 			} catch (InvalidOperationException ex) {
 				// This is a case where the driver is unable to initialize the console.
 				// This can happen if the console is already in use by another process or
@@ -207,7 +185,13 @@ namespace Terminal.Gui {
 				throw new InvalidOperationException ("Unable to initialize the console. This can happen if the console is already in use by another process or in unit tests.", ex);
 			}
 
-			SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext (MainLoop));
+			Driver.SizeChanged += (s, args) => OnSizeChanging (args);
+			Driver.KeyPressed += (s, args) => OnKeyPressed (args);
+			Driver.KeyDown += (s, args) => OnKeyDown (args);
+			Driver.KeyUp += (s, args) => OnKeyUp (args);
+			Driver.MouseEvent += (s, args) => OnMouseEvent (args);
+
+			SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext ());
 
 			Top = topLevelFactory ();
 			Current = Top;
@@ -218,10 +202,10 @@ namespace Terminal.Gui {
 
 
 		/// <summary>
-		/// Shutdown an application initialized with <see cref="Init(ConsoleDriver, IMainLoopDriver)"/>.
+		/// Shutdown an application initialized with <see cref="Init(ConsoleDriver)"/>.
 		/// </summary>
 		/// <remarks>
-		/// Shutdown must be called for every call to <see cref="Init(ConsoleDriver, IMainLoopDriver)"/> or <see cref="Application.Run(Toplevel, Func{Exception, bool})"/>
+		/// Shutdown must be called for every call to <see cref="Init(ConsoleDriver)"/> or <see cref="Application.Run(Toplevel, Func{Exception, bool})"/>
 		/// to ensure all resources are cleaned up (Disposed) and terminal settings are restored.
 		/// </remarks>
 		public static void Shutdown ()
@@ -239,11 +223,11 @@ namespace Terminal.Gui {
 			// Shutdown is the bookend for Init. As such it needs to clean up all resources
 			// Init created. Apps that do any threading will need to code defensively for this.
 			// e.g. see Issue #537
-			foreach (var t in _toplevels) {
+			foreach (var t in _topLevels) {
 				t.Running = false;
 				t.Dispose ();
 			}
-			_toplevels.Clear ();
+			_topLevels.Clear ();
 			Current = null;
 			Top?.Dispose ();
 			Top = null;
@@ -255,9 +239,11 @@ namespace Terminal.Gui {
 			Driver?.End ();
 			Driver = null;
 			Iteration = null;
-			RootMouseEvent = null;
-			RootKeyEvent = null;
-			TerminalResized = null;
+			MouseEvent = null;
+			KeyDown = null;
+			KeyUp = null;
+			KeyPressed = null;
+			SizeChanging = null;
 			_mainThreadId = -1;
 			NotifyNewRunState = null;
 			NotifyStopRunState = null;
@@ -274,14 +260,14 @@ namespace Terminal.Gui {
 
 		#endregion Initialization (Init/Shutdown)
 
-		#region Run (Begin, Run, End)
+		#region Run (Begin, Run, End, Stop)
 
 		/// <summary>
 		/// Notify that a new <see cref="RunState"/> was created (<see cref="Begin(Toplevel)"/> was called). The token is created in 
 		/// <see cref="Begin(Toplevel)"/> and this event will be fired before that function exits.
 		/// </summary>
 		/// <remarks>
-		///	If <see cref="ExitRunLoopAfterFirstIteration"/> is <see langword="true"/> callers to
+		///	If <see cref="EndAfterFirstIteration"/> is <see langword="true"/> callers to
 		///	<see cref="Begin(Toplevel)"/> must also subscribe to <see cref="NotifyStopRunState"/>
 		///	and manually dispose of the <see cref="RunState"/> token when the application is done.
 		/// </remarks>
@@ -291,7 +277,7 @@ namespace Terminal.Gui {
 		/// Notify that a existent <see cref="RunState"/> is stopping (<see cref="End(RunState)"/> was called).
 		/// </summary>
 		/// <remarks>
-		///	If <see cref="ExitRunLoopAfterFirstIteration"/> is <see langword="true"/> callers to
+		///	If <see cref="EndAfterFirstIteration"/> is <see langword="true"/> callers to
 		///	<see cref="Begin(Toplevel)"/> must also subscribe to <see cref="NotifyStopRunState"/>
 		///	and manually dispose of the <see cref="RunState"/> token when the application is done.
 		/// </remarks>
@@ -304,8 +290,7 @@ namespace Terminal.Gui {
 		/// <param name="Toplevel">The <see cref="Toplevel"/> to prepare execution for.</param>
 		/// <remarks>
 		/// This method prepares the provided <see cref="Toplevel"/> for running with the focus,
-		/// it adds this to the list of <see cref="Toplevel"/>s, sets up the <see cref="MainLoop"/> to process the
-		/// event, lays out the Subviews, focuses the first element, and draws the
+		/// it adds this to the list of <see cref="Toplevel"/>s, lays out the Subviews, focuses the first element, and draws the
 		/// <see cref="Toplevel"/> in the screen. This is usually followed by executing
 		/// the <see cref="RunLoop"/> method, and then the <see cref="End(RunState)"/> method upon termination which will
 		///  undo these changes.
@@ -329,34 +314,34 @@ namespace Terminal.Gui {
 				Toplevel.EndInit ();
 			}
 
-			lock (_toplevels) {
+			lock (_topLevels) {
 				// If Top was already initialized with Init, and Begin has never been called
 				// Top was not added to the Toplevels Stack. It will thus never get disposed.
 				// Clean it up here:
-				if (Top != null && Toplevel != Top && !_toplevels.Contains (Top)) {
+				if (Top != null && Toplevel != Top && !_topLevels.Contains (Top)) {
 					Top.Dispose ();
 					Top = null;
-				} else if (Top != null && Toplevel != Top && _toplevels.Contains (Top)) {
+				} else if (Top != null && Toplevel != Top && _topLevels.Contains (Top)) {
 					Top.OnLeave (Toplevel);
 				}
 				if (string.IsNullOrEmpty (Toplevel.Id)) {
 					var count = 1;
-					var id = (_toplevels.Count + count).ToString ();
-					while (_toplevels.Count > 0 && _toplevels.FirstOrDefault (x => x.Id == id) != null) {
+					var id = (_topLevels.Count + count).ToString ();
+					while (_topLevels.Count > 0 && _topLevels.FirstOrDefault (x => x.Id == id) != null) {
 						count++;
-						id = (_toplevels.Count + count).ToString ();
+						id = (_topLevels.Count + count).ToString ();
 					}
-					Toplevel.Id = (_toplevels.Count + count).ToString ();
+					Toplevel.Id = (_topLevels.Count + count).ToString ();
 
-					_toplevels.Push (Toplevel);
+					_topLevels.Push (Toplevel);
 				} else {
-					var dup = _toplevels.FirstOrDefault (x => x.Id == Toplevel.Id);
+					var dup = _topLevels.FirstOrDefault (x => x.Id == Toplevel.Id);
 					if (dup == null) {
-						_toplevels.Push (Toplevel);
+						_topLevels.Push (Toplevel);
 					}
 				}
 
-				if (_toplevels.FindDuplicates (new ToplevelEqualityComparer ()).Count > 0) {
+				if (_topLevels.FindDuplicates (new ToplevelEqualityComparer ()).Count > 0) {
 					throw new ArgumentException ("There are duplicates Toplevels Id's");
 				}
 			}
@@ -374,7 +359,7 @@ namespace Terminal.Gui {
 				} else {
 					refreshDriver = false;
 				}
-			} else if ((OverlappedTop != null && Toplevel != OverlappedTop && Current?.Modal == true && !_toplevels.Peek ().Modal)
+			} else if ((OverlappedTop != null && Toplevel != OverlappedTop && Current?.Modal == true && !_topLevels.Peek ().Modal)
 				|| (OverlappedTop != null && Toplevel != OverlappedTop && Current?.Running == false)) {
 				refreshDriver = false;
 				MoveCurrent (Toplevel);
@@ -383,7 +368,6 @@ namespace Terminal.Gui {
 				MoveCurrent (Current);
 			}
 
-			Driver.PrepareToRun (MainLoop, ProcessKeyEvent, ProcessKeyDownEvent, ProcessKeyUpEvent, ProcessMouseEvent);
 			if (Toplevel.LayoutStyle == LayoutStyle.Computed) {
 				Toplevel.SetRelativeLayout (new Rect (0, 0, Driver.Cols, Driver.Rows));
 			}
@@ -415,7 +399,7 @@ namespace Terminal.Gui {
 		/// Runs the application by calling <see cref="Run(Toplevel, Func{Exception, bool})"/> 
 		/// with a new instance of the specified <see cref="Toplevel"/>-derived class.
 		/// <para>
-		/// Calling <see cref="Init(ConsoleDriver, IMainLoopDriver)"/> first is not needed as this function will initialize the application.
+		/// Calling <see cref="Init(ConsoleDriver)"/> first is not needed as this function will initialize the application.
 		/// </para>
 		/// <para>
 		/// <see cref="Shutdown"/> must be called when the application is closing (typically after Run> has 
@@ -428,10 +412,9 @@ namespace Terminal.Gui {
 		/// <param name="errorHandler"></param>
 		/// <param name="driver">The <see cref="ConsoleDriver"/> to use. If not specified the default driver for the
 		/// platform will be used (<see cref="WindowsDriver"/>, <see cref="CursesDriver"/>, or <see cref="NetDriver"/>).
-		/// Must be <see langword="null"/> if <see cref="Init(ConsoleDriver, IMainLoopDriver)"/> has already been called. 
+		/// Must be <see langword="null"/> if <see cref="Init(ConsoleDriver)"/> has already been called. 
 		/// </param>
-		/// <param name="mainLoopDriver">Specifies the <see cref="MainLoop"/> to use.</param>
-		public static void Run<T> (Func<Exception, bool> errorHandler = null, ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null) where T : Toplevel, new()
+		public static void Run<T> (Func<Exception, bool> errorHandler = null, ConsoleDriver driver = null) where T : Toplevel, new()
 		{
 			if (_initialized) {
 				if (Driver != null) {
@@ -451,7 +434,7 @@ namespace Terminal.Gui {
 				}
 			} else {
 				// Init() has NOT been called.
-				InternalInit (() => new T (), driver, mainLoopDriver, calledViaRunT: true);
+				InternalInit (() => new T (), driver, calledViaRunT: true);
 				Run (Top, errorHandler);
 			}
 		}
@@ -496,12 +479,12 @@ namespace Terminal.Gui {
 				try {
 #endif
 				resume = false;
-				var runToken = Begin (view);
+				var runState = Begin (view);
 				// If ExitRunLoopAfterFirstIteration is true then the user must dispose of the runToken
 				// by using NotifyStopRunState event.
-				RunLoop (runToken);
-				if (!ExitRunLoopAfterFirstIteration) {
-					End (runToken);
+				RunLoop (runState);
+				if (!EndAfterFirstIteration) {
+					End (runState);
 				}
 #if !DEBUG
 				}
@@ -517,6 +500,49 @@ namespace Terminal.Gui {
 			}
 		}
 
+		/// <summary>
+		///   Adds a timeout to the application.
+		/// </summary>
+		/// <remarks>
+		///   When time specified passes, the callback will be invoked.
+		///   If the callback returns true, the timeout will be reset, repeating
+		///   the invocation. If it returns false, the timeout will stop and be removed.
+		///
+		///   The returned value is a token that can be used to stop the timeout
+		///   by calling <see cref="RemoveTimeout(object)"/>.
+		/// </remarks>
+		public static object AddTimeout (TimeSpan time, Func<bool> callback) => MainLoop?.AddTimeout (time, callback);
+
+		/// <summary>
+		///   Removes a previously scheduled timeout
+		/// </summary>
+		/// <remarks>
+		///   The token parameter is the value returned by <see cref="AddTimeout"/>.
+		/// </remarks>
+		/// Returns <c>true</c>if the timeout is successfully removed; otherwise, <c>false</c>.
+		/// This method also returns <c>false</c> if the timeout is not found.
+		public static bool RemoveTimeout (object token) => MainLoop?.RemoveTimeout (token) ?? false;
+
+
+		/// <summary>
+		///   Runs <paramref name="action"/> on the thread that is processing events
+		/// </summary>
+		/// <param name="action">the action to be invoked on the main processing thread.</param>
+		public static void Invoke (Action action)
+		{
+			MainLoop?.AddIdle (() => {
+				action ();
+				return false;
+			});
+		}
+
+		// 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.
+		/// <summary>
+		/// Wakes up the running application that might be waiting on input.
+		/// </summary>
+		public static void Wakeup () => MainLoop?.Wakeup ();
+
 		/// <summary>
 		/// Triggers a refresh of the entire display.
 		/// </summary>
@@ -525,7 +551,7 @@ namespace Terminal.Gui {
 			// TODO: Figure out how to remove this call to ClearContents. Refresh should just repaint damaged areas, not clear
 			Driver.ClearContents ();
 			View last = null;
-			foreach (var v in _toplevels.Reverse ()) {
+			foreach (var v in _topLevels.Reverse ()) {
 				if (v.Visible) {
 					v.SetNeedsDisplay ();
 					v.SetSubViewNeedsDisplay ();
@@ -543,40 +569,33 @@ namespace Terminal.Gui {
 		/// <remarks>
 		///  See also <see cref="Timeout"/>
 		/// </remarks>
-		public static Action Iteration;
+		public static event EventHandler<IterationEventArgs> Iteration;
 
 		/// <summary>
 		/// The <see cref="MainLoop"/> driver for the application
 		/// </summary>
 		/// <value>The main loop.</value>
-		public static MainLoop MainLoop { get; private set; }
+		internal static MainLoop MainLoop { get; private set; }
 
 		/// <summary>
-		/// Set to true to cause the RunLoop method to exit after the first iterations.
-		/// Set to false (the default) to cause the RunLoop to continue running until Application.RequestStop() is called.
+		/// Set to true to cause <see cref="End"/> to be called after the first iteration.
+		/// Set to false (the default) to cause the application to continue running until Application.RequestStop () is called.
 		/// </summary>
-		public static bool ExitRunLoopAfterFirstIteration { get; set; } = false;
+		public static bool EndAfterFirstIteration { get; set; } = false;
 
 		//
 		// provides the sync context set while executing code in Terminal.Gui, to let
 		// users use async/await on their code
 		//
 		class MainLoopSyncContext : SynchronizationContext {
-			readonly MainLoop _mainLoop;
-
-			public MainLoopSyncContext (MainLoop mainLoop)
-			{
-				this._mainLoop = mainLoop;
-			}
-
 			public override SynchronizationContext CreateCopy ()
 			{
-				return new MainLoopSyncContext (MainLoop);
+				return new MainLoopSyncContext ();
 			}
 
 			public override void Post (SendOrPostCallback d, object state)
 			{
-				_mainLoop.AddIdle (() => {
+				MainLoop.AddIdle (() => {
 					d (state);
 					return false;
 				});
@@ -589,7 +608,7 @@ namespace Terminal.Gui {
 					d (state);
 				} else {
 					var wasExecuted = false;
-					_mainLoop.Invoke (() => {
+					Invoke (() => {
 						d (state);
 						wasExecuted = true;
 					});
@@ -615,20 +634,20 @@ namespace Terminal.Gui {
 
 			var firstIteration = true;
 			for (state.Toplevel.Running = true; state.Toplevel.Running;) {
-				if (ExitRunLoopAfterFirstIteration && !firstIteration) {
+				if (EndAfterFirstIteration && !firstIteration) {
 					return;
 				}
-				RunMainLoopIteration (ref state, ref firstIteration);
+				RunIteration (ref state, ref firstIteration);
 			}
 		}
 
 		/// <summary>
-		/// Run one iteration of the <see cref="MainLoop"/>.
+		/// Run one application iteration.
 		/// </summary>
 		/// <param name="state">The state returned by <see cref="Begin(Toplevel)"/>.</param>
 		/// <param name="firstIteration">Set to <see langword="true"/> if this is the first run loop iteration. Upon return,
 		/// it will be set to <see langword="false"/> if at least one iteration happened.</param>
-		public static void RunMainLoopIteration (ref RunState state, ref bool firstIteration)
+		public static void RunIteration (ref RunState state, ref bool firstIteration)
 		{
 			if (MainLoop.EventsPending ()) {
 				// Notify Toplevel it's ready
@@ -637,7 +656,7 @@ namespace Terminal.Gui {
 				}
 
 				MainLoop.RunIteration ();
-				Iteration?.Invoke ();
+				Iteration?.Invoke (null, new IterationEventArgs());
 
 				EnsureModalOrVisibleAlwaysOnTop (state.Toplevel);
 				if (state.Toplevel != Current) {
@@ -661,7 +680,7 @@ namespace Terminal.Gui {
 				state.Toplevel.SetNeedsDisplay (state.Toplevel.Frame);
 				Top.Clear ();
 				Top.Draw ();
-				foreach (var top in _toplevels.Reverse ()) {
+				foreach (var top in _topLevels.Reverse ()) {
 					if (top != Top && top != state.Toplevel) {
 						top.SetNeedsDisplay ();
 						top.SetSubViewNeedsDisplay ();
@@ -670,7 +689,7 @@ namespace Terminal.Gui {
 					}
 				}
 			}
-			if (_toplevels.Count == 1 && state.Toplevel == Top
+			if (_topLevels.Count == 1 && state.Toplevel == Top
 				&& (Driver.Cols != state.Toplevel.Frame.Width || Driver.Rows != state.Toplevel.Frame.Height)
 				&& (state.Toplevel.NeedsDisplay || state.Toplevel.SubViewNeedsDisplay || state.Toplevel.LayoutNeeded)) {
 
@@ -738,7 +757,7 @@ namespace Terminal.Gui {
 			} else if ((OverlappedTop != null && top != OverlappedTop && top != Current && Current?.Modal == false
 				&& Current?.Running == true && !top.Running)
 				|| (OverlappedTop != null && top != OverlappedTop && top != Current && Current?.Modal == false
-				&& Current?.Running == false && !top.Running && _toplevels.ToArray () [1].Running)) {
+				&& Current?.Running == false && !top.Running && _topLevels.ToArray () [1].Running)) {
 
 				MoveCurrent (top);
 			} else if (OverlappedTop != null && Current != top && Current?.Running == true && !top.Running
@@ -774,7 +793,7 @@ namespace Terminal.Gui {
 
 		static void OnNotifyStopRunState (Toplevel top)
 		{
-			if (ExitRunLoopAfterFirstIteration) {
+			if (EndAfterFirstIteration) {
 				NotifyStopRunState?.Invoke (top, new ToplevelEventArgs (top));
 			}
 		}
@@ -785,8 +804,9 @@ namespace Terminal.Gui {
 		/// <param name="runState">The <see cref="RunState"/> returned by the <see cref="Begin(Toplevel)"/> method.</param>
 		public static void End (RunState runState)
 		{
-			if (runState == null)
+			if (runState == null) {
 				throw new ArgumentNullException (nameof (runState));
+			}
 
 			if (OverlappedTop != null) {
 				OverlappedTop.OnChildUnloaded (runState.Toplevel);
@@ -796,13 +816,13 @@ namespace Terminal.Gui {
 
 			// End the RunState.Toplevel 
 			// First, take it off the Toplevel Stack
-			if (_toplevels.Count > 0) {
-				if (_toplevels.Peek () != runState.Toplevel) {
+			if (_topLevels.Count > 0) {
+				if (_topLevels.Peek () != runState.Toplevel) {
 					// If there the top of the stack is not the RunState.Toplevel then
 					// this call to End is not balanced with the call to Begin that started the RunState
 					throw new ArgumentException ("End must be balanced with calls to Begin");
 				}
-				_toplevels.Pop ();
+				_topLevels.Pop ();
 			}
 
 			// Notify that it is closing
@@ -815,11 +835,11 @@ namespace Terminal.Gui {
 			}
 
 			// Set Current and Top to the next TopLevel on the stack
-			if (_toplevels.Count == 0) {
+			if (_topLevels.Count == 0) {
 				Current = null;
 			} else {
-				Current = _toplevels.Peek ();
-				if (_toplevels.Count == 1 && Current == OverlappedTop) {
+				Current = _topLevels.Peek ();
+				if (_topLevels.Count == 1 && Current == OverlappedTop) {
 					OverlappedTop.OnAllChildClosed ();
 				} else {
 					SetCurrentOverlappedAsTop ();
@@ -837,7 +857,7 @@ namespace Terminal.Gui {
 		#endregion Run (Begin, Run, End)
 
 		#region Toplevel handling
-		static readonly Stack<Toplevel> _toplevels = new Stack<Toplevel> ();
+		static readonly Stack<Toplevel> _topLevels = new Stack<Toplevel> ();
 
 		/// <summary>
 		/// The <see cref="Toplevel"/> object used for the application on startup (<seealso cref="Application.Top"/>)
@@ -854,11 +874,11 @@ namespace Terminal.Gui {
 
 		static void EnsureModalOrVisibleAlwaysOnTop (Toplevel Toplevel)
 		{
-			if (!Toplevel.Running || (Toplevel == Current && Toplevel.Visible) || OverlappedTop == null || _toplevels.Peek ().Modal) {
+			if (!Toplevel.Running || (Toplevel == Current && Toplevel.Visible) || OverlappedTop == null || _topLevels.Peek ().Modal) {
 				return;
 			}
 
-			foreach (var top in _toplevels.Reverse ()) {
+			foreach (var top in _topLevels.Reverse ()) {
 				if (top.Modal && top != Current) {
 					MoveCurrent (top);
 					return;
@@ -879,12 +899,12 @@ namespace Terminal.Gui {
 				return null;
 			}
 
-			if (_toplevels != null) {
-				int count = _toplevels.Count;
+			if (_topLevels != null) {
+				int count = _topLevels.Count;
 				if (count > 0) {
 					var rx = x - startFrame.X;
 					var ry = y - startFrame.Y;
-					foreach (var t in _toplevels) {
+					foreach (var t in _topLevels) {
 						if (t != Current) {
 							if (t != start && t.Visible && t.Frame.Contains (rx, ry)) {
 								start = t;
@@ -915,16 +935,16 @@ namespace Terminal.Gui {
 		{
 			// The Current is modal and the top is not modal Toplevel then
 			// the Current must be moved above the first not modal Toplevel.
-			if (OverlappedTop != null && top != OverlappedTop && top != Current && Current?.Modal == true && !_toplevels.Peek ().Modal) {
-				lock (_toplevels) {
-					_toplevels.MoveTo (Current, 0, new ToplevelEqualityComparer ());
+			if (OverlappedTop != null && top != OverlappedTop && top != Current && Current?.Modal == true && !_topLevels.Peek ().Modal) {
+				lock (_topLevels) {
+					_topLevels.MoveTo (Current, 0, new ToplevelEqualityComparer ());
 				}
 				var index = 0;
-				var savedToplevels = _toplevels.ToArray ();
+				var savedToplevels = _topLevels.ToArray ();
 				foreach (var t in savedToplevels) {
 					if (!t.Modal && t != Current && t != top && t != savedToplevels [index]) {
-						lock (_toplevels) {
-							_toplevels.MoveTo (top, index, new ToplevelEqualityComparer ());
+						lock (_topLevels) {
+							_topLevels.MoveTo (top, index, new ToplevelEqualityComparer ());
 						}
 					}
 					index++;
@@ -934,26 +954,26 @@ namespace Terminal.Gui {
 			// The Current and the top are both not running Toplevel then
 			// the top must be moved above the first not running Toplevel.
 			if (OverlappedTop != null && top != OverlappedTop && top != Current && Current?.Running == false && !top.Running) {
-				lock (_toplevels) {
-					_toplevels.MoveTo (Current, 0, new ToplevelEqualityComparer ());
+				lock (_topLevels) {
+					_topLevels.MoveTo (Current, 0, new ToplevelEqualityComparer ());
 				}
 				var index = 0;
-				foreach (var t in _toplevels.ToArray ()) {
+				foreach (var t in _topLevels.ToArray ()) {
 					if (!t.Running && t != Current && index > 0) {
-						lock (_toplevels) {
-							_toplevels.MoveTo (top, index - 1, new ToplevelEqualityComparer ());
+						lock (_topLevels) {
+							_topLevels.MoveTo (top, index - 1, new ToplevelEqualityComparer ());
 						}
 					}
 					index++;
 				}
 				return false;
 			}
-			if ((OverlappedTop != null && top?.Modal == true && _toplevels.Peek () != top)
+			if ((OverlappedTop != null && top?.Modal == true && _topLevels.Peek () != top)
 				|| (OverlappedTop != null && Current != OverlappedTop && Current?.Modal == false && top == OverlappedTop)
 				|| (OverlappedTop != null && Current?.Modal == false && top != Current)
 				|| (OverlappedTop != null && Current?.Modal == true && top == OverlappedTop)) {
-				lock (_toplevels) {
-					_toplevels.MoveTo (top, 0, new ToplevelEqualityComparer ());
+				lock (_topLevels) {
+					_topLevels.MoveTo (top, 0, new ToplevelEqualityComparer ());
 					Current = top;
 				}
 			}
@@ -961,21 +981,35 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Invoked when the terminal was resized. The new size of the terminal is provided.
+		/// Invoked when the terminal's size changed. The new size of the terminal is provided.
 		/// </summary>
-		public static Action<ResizedEventArgs> TerminalResized;
+		/// <remarks>
+		/// Event handlers can set <see cref="SizeChangedEventArgs.Cancel"/> to <see langword="true"/>
+		/// to prevent <see cref="Application"/> from changing it's size to match the new terminal size.
+		/// </remarks>
+		public static event EventHandler<SizeChangedEventArgs> SizeChanging;
 
-		static void OnTerminalResized ()
+		/// <summary>
+		/// Called when the application's size changes. Sets the size of all <see cref="Toplevel"/>s and
+		/// fires the <see cref="SizeChanging"/> event.
+		/// </summary>
+		/// <param name="args">The new size.</param>
+		/// <returns><see lanword="true"/>if the size was changed.</returns>
+		public static bool OnSizeChanging (SizeChangedEventArgs args)
 		{
-			var full = new Rect (0, 0, Driver.Cols, Driver.Rows);
-			TerminalResized?.Invoke (new ResizedEventArgs () { Cols = full.Width, Rows = full.Height });
-			foreach (var t in _toplevels) {
-				t.SetRelativeLayout (full);
+			SizeChanging?.Invoke (null, args);
+			if (args.Cancel) {
+				return false;
+			}
+
+			foreach (var t in _topLevels) {
+				t.SetRelativeLayout (new Rect (0, 0, args.Size.Width, args.Size.Height));
 				t.LayoutSubviews ();
 				t.PositionToplevels ();
-				t.OnTerminalResized (new SizeChangedEventArgs (full.Size));
+				t.OnSizeChanging (new SizeChangedEventArgs (args.Size));
 			}
 			Refresh ();
+			return true;
 		}
 
 		#endregion Toplevel handling
@@ -1080,14 +1114,30 @@ namespace Terminal.Gui {
 			UnGrabbedMouse?.Invoke (view, new ViewEventArgs (view));
 		}
 
+		static View _lastMouseOwnerView;
+
 		/// <summary>
-		/// Merely a debugging aid to see the raw mouse events
+		/// Event fired when a mouse move or click occurs. Coordinates are screen relative.
 		/// </summary>
-		public static Action<MouseEvent> RootMouseEvent { get; set; }
-
-		static View _lastMouseOwnerView;
+		/// <remarks>
+		/// <para>
+		/// Use this event to receive mouse events in screen coordinates. Use <see cref="Responder.MouseEvent"/> to receive
+		/// mouse events relative to a <see cref="View"/>'s bounds.
+		/// </para>
+		/// <para>
+		/// The <see cref="MouseEvent.View"/> will contain the <see cref="View"/> that contains the mouse coordinates.
+		/// </para>
+		/// </remarks>
+		public static event EventHandler<MouseEventEventArgs> MouseEvent;
 
-		static void ProcessMouseEvent (MouseEvent me)
+		/// <summary>
+		/// Called when a mouse event occurs. Fires the <see cref="MouseEvent"/> event.
+		/// </summary>
+		/// <remarks>
+		/// This method can be used to simulate a mouse event, e.g. in unit tests.
+		/// </remarks>
+		/// <param name="a"></param>
+		public static void OnMouseEvent (MouseEventEventArgs a)
 		{
 			static bool OutsideBounds (Point p, Rect r) => p.X < 0 || p.X > r.Right || p.Y < 0 || p.Y > r.Bottom;
 
@@ -1095,7 +1145,7 @@ namespace Terminal.Gui {
 				return;
 			}
 
-			var view = View.FindDeepestView (Current, me.X, me.Y, out int rx, out int ry);
+			var view = View.FindDeepestView (Current, a.MouseEvent.X, a.MouseEvent.Y, out int rx, out int ry);
 
 			if (view != null && view.WantContinuousButtonPressed) {
 				WantContinuousButtonPressedView = view;
@@ -1103,26 +1153,26 @@ namespace Terminal.Gui {
 				WantContinuousButtonPressedView = null;
 			}
 			if (view != null) {
-				me.View = view;
+				a.MouseEvent.View = view;
 			}
-			RootMouseEvent?.Invoke (me);
+			MouseEvent?.Invoke (null, new MouseEventEventArgs (a.MouseEvent));
 
-			if (me.Handled) {
+			if (a.MouseEvent.Handled) {
 				return;
 			}
 
 			if (_mouseGrabView != null) {
-				var newxy = _mouseGrabView.ScreenToView (me.X, me.Y);
+				var newxy = _mouseGrabView.ScreenToView (a.MouseEvent.X, a.MouseEvent.Y);
 				var nme = new MouseEvent () {
 					X = newxy.X,
 					Y = newxy.Y,
-					Flags = me.Flags,
-					OfX = me.X - newxy.X,
-					OfY = me.Y - newxy.Y,
+					Flags = a.MouseEvent.Flags,
+					OfX = a.MouseEvent.X - newxy.X,
+					OfY = a.MouseEvent.Y - newxy.Y,
 					View = view
 				};
 				if (OutsideBounds (new Point (nme.X, nme.Y), _mouseGrabView.Bounds)) {
-					_lastMouseOwnerView?.OnMouseLeave (me);
+					_lastMouseOwnerView?.OnMouseLeave (a.MouseEvent);
 				}
 				//System.Diagnostics.Debug.WriteLine ($"{nme.Flags};{nme.X};{nme.Y};{mouseGrabView}");
 				if (_mouseGrabView?.OnMouseEvent (nme) == true) {
@@ -1131,10 +1181,10 @@ namespace Terminal.Gui {
 			}
 
 			if ((view == null || view == OverlappedTop) && !Current.Modal && OverlappedTop != null
-				&& me.Flags != MouseFlags.ReportMousePosition && me.Flags != 0) {
+				&& a.MouseEvent.Flags != MouseFlags.ReportMousePosition && a.MouseEvent.Flags != 0) {
 
-				var top = FindDeepestTop (Top, me.X, me.Y, out _, out _);
-				view = View.FindDeepestView (top, me.X, me.Y, out rx, out ry);
+				var top = FindDeepestTop (Top, a.MouseEvent.X, a.MouseEvent.Y, out _, out _);
+				view = View.FindDeepestView (top, a.MouseEvent.X, a.MouseEvent.Y, out rx, out ry);
 
 				if (view != null && view != OverlappedTop && top != Current) {
 					MoveCurrent ((Toplevel)top);
@@ -1145,7 +1195,7 @@ namespace Terminal.Gui {
 				var nme = new MouseEvent () {
 					X = rx,
 					Y = ry,
-					Flags = me.Flags,
+					Flags = a.MouseEvent.Flags,
 					OfX = 0,
 					OfY = 0,
 					View = view
@@ -1160,7 +1210,7 @@ namespace Terminal.Gui {
 					_lastMouseOwnerView = view;
 				}
 
-				if (!view.WantMousePositionReports && me.Flags == MouseFlags.ReportMousePosition)
+				if (!view.WantMousePositionReports && a.MouseEvent.Flags == MouseFlags.ReportMousePosition)
 					return;
 
 				if (view.WantContinuousButtonPressed)
@@ -1178,7 +1228,6 @@ namespace Terminal.Gui {
 
 		#region Keyboard handling
 
-
 		static Key _alternateForwardKey = Key.PageDown | Key.CtrlMask;
 
 		/// <summary>
@@ -1198,7 +1247,7 @@ namespace Terminal.Gui {
 
 		static void OnAlternateForwardKeyChanged (KeyChangedEventArgs e)
 		{
-			foreach (var top in _toplevels.ToArray ()) {
+			foreach (var top in _topLevels.ToArray ()) {
 				top.OnAlternateForwardKeyChanged (e);
 			}
 		}
@@ -1222,7 +1271,7 @@ namespace Terminal.Gui {
 
 		static void OnAlternateBackwardKeyChanged (KeyChangedEventArgs oldKey)
 		{
-			foreach (var top in _toplevels.ToArray ()) {
+			foreach (var top in _topLevels.ToArray ()) {
 				top.OnAlternateBackwardKeyChanged (oldKey);
 			}
 		}
@@ -1246,72 +1295,122 @@ namespace Terminal.Gui {
 		static void OnQuitKeyChanged (KeyChangedEventArgs e)
 		{
 			// Duplicate the list so if it changes during enumeration we're safe
-			foreach (var top in _toplevels.ToArray ()) {
+			foreach (var top in _topLevels.ToArray ()) {
 				top.OnQuitKeyChanged (e);
 			}
 		}
 
-		static void ProcessKeyEvent (KeyEvent ke)
+		/// <summary>
+		/// Event fired after a key has been pressed and released.
+		/// <para>Set <see cref="KeyEventEventArgs.Handled"/> to <see langword="true"/> to suppress the event.</para>
+		/// </summary>
+		/// <remarks>
+		/// All drivers support firing the <see cref="KeyPressed"/> event. Some drivers (Curses)
+		/// do not support firing the <see cref="KeyDown"/> and <see cref="KeyUp"/> events.
+		/// </remarks>
+		public static event EventHandler<KeyEventEventArgs> KeyPressed;
+
+		/// <summary>
+		/// Called after a key has been pressed and released. Fires the <see cref="KeyPressed"/> event.
+		/// <para>
+		/// Called for new KeyPressed events before any processing is performed or
+		/// views evaluate. Use for global key handling and/or debugging.
+		/// </para>
+		/// </summary>
+		/// <param name="a"></param>
+		/// <returns><see langword="true"/> if the key was handled.</returns>
+		public static bool OnKeyPressed (KeyEventEventArgs a)
 		{
-			if (RootKeyEvent?.Invoke (ke) ?? false) {
-				return;
+			KeyPressed?.Invoke (null, a);
+			if (a.Handled) {
+				return true;
 			}
 
-			var chain = _toplevels.ToList ();
+			var chain = _topLevels.ToList ();
 			foreach (var topLevel in chain) {
-				if (topLevel.ProcessHotKey (ke))
-					return;
+				if (topLevel.ProcessHotKey (a.KeyEvent)) {
+					return true;
+				}
 				if (topLevel.Modal)
 					break;
 			}
 
 			foreach (var topLevel in chain) {
-				if (topLevel.ProcessKey (ke))
-					return;
+				if (topLevel.ProcessKey (a.KeyEvent)) {
+					return true;
+				}
 				if (topLevel.Modal)
 					break;
 			}
 
 			foreach (var topLevel in chain) {
 				// Process the key normally
-				if (topLevel.ProcessColdKey (ke))
-					return;
+				if (topLevel.ProcessColdKey (a.KeyEvent)) {
+					return true;
+				}
 				if (topLevel.Modal)
 					break;
 			}
+			return false;
 		}
 
-		static void ProcessKeyDownEvent (KeyEvent ke)
+		/// <summary>
+		/// Event fired when a key is pressed (and not yet released). 
+		/// </summary>
+		/// <remarks>
+		/// All drivers support firing the <see cref="KeyPressed"/> event. Some drivers (Curses)
+		/// do not support firing the <see cref="KeyDown"/> and <see cref="KeyUp"/> events.
+		/// </remarks>
+		public static event EventHandler<KeyEventEventArgs> KeyDown;
+
+		/// <summary>
+		/// Called when a key is pressed (and not yet released). Fires the <see cref="KeyDown"/> event.
+		/// </summary>
+		/// <param name="a"></param>
+		public static void OnKeyDown (KeyEventEventArgs a)
 		{
-			var chain = _toplevels.ToList ();
+			KeyDown?.Invoke (null, a);
+			var chain = _topLevels.ToList ();
 			foreach (var topLevel in chain) {
-				if (topLevel.OnKeyDown (ke))
+				if (topLevel.OnKeyDown (a.KeyEvent))
 					return;
 				if (topLevel.Modal)
 					break;
 			}
 		}
 
-		static void ProcessKeyUpEvent (KeyEvent ke)
+		/// <summary>
+		/// Event fired when a key is released. 
+		/// </summary>
+		/// <remarks>
+		/// All drivers support firing the <see cref="KeyPressed"/> event. Some drivers (Curses)
+		/// do not support firing the <see cref="KeyDown"/> and <see cref="KeyUp"/> events.
+		/// </remarks>
+		public static event EventHandler<KeyEventEventArgs> KeyUp;
+
+		/// <summary>
+		/// Called when a key is released. Fires the <see cref="KeyUp"/> event.
+		/// </summary>
+		/// <param name="a"></param>
+		public static void OnKeyUp (KeyEventEventArgs a)
 		{
-			var chain = _toplevels.ToList ();
+			KeyUp?.Invoke (null, a);
+			var chain = _topLevels.ToList ();
 			foreach (var topLevel in chain) {
-				if (topLevel.OnKeyUp (ke))
+				if (topLevel.OnKeyUp (a.KeyEvent))
 					return;
 				if (topLevel.Modal)
 					break;
 			}
-		}
 
-		/// <summary>
-		/// <para>
-		/// Called for new KeyPress events before any processing is performed or
-		/// views evaluate. Use for global key handling and/or debugging.
-		/// </para>
-		/// <para>Return true to suppress the KeyPress event</para>
-		/// </summary>
-		public static Func<KeyEvent, bool> RootKeyEvent { get; set; }
+		}
 
 		#endregion Keyboard handling
 	}
+
+	/// <summary>
+	/// Event arguments for the <see cref="Application.Iteration"/> event.
+	/// </summary>
+	public class IterationEventArgs {
+	}
 }

+ 1 - 1
Terminal.Gui/Configuration/ConfigurationManager.cs

@@ -251,7 +251,7 @@ public static partial class ConfigurationManager {
 
 	/// <summary>
 	/// Resets the state of <see cref="ConfigurationManager"/>. Should be called whenever a new app session
-	/// (e.g. in <see cref="Application.Init(ConsoleDriver, IMainLoopDriver)"/> starts. Called by <see cref="Load"/>
+	/// (e.g. in <see cref="Application.Init(ConsoleDriver)"/> starts. Called by <see cref="Load"/>
 	/// if the <c>reset</c> parameter is <see langword="true"/>.
 	/// </summary>
 	/// <remarks>

+ 81 - 43
Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs

@@ -9,7 +9,6 @@ using static Terminal.Gui.ColorScheme;
 
 namespace Terminal.Gui;
 
-
 /// <summary>
 /// Base class for Terminal.Gui ConsoleDriver implementations.
 /// </summary>
@@ -32,20 +31,31 @@ public abstract class ConsoleDriver {
 	/// </summary>
 	internal static bool RunningUnitTests { get; set; }
 
+	#region Setup & Teardown
+
+	/// <summary>
+	/// Initializes the driver
+	/// </summary>
+	/// <returns>Returns an instance of <see cref="MainLoop"/> using the <see cref="IMainLoopDriver"/> for the driver.</returns>
+	internal abstract MainLoop Init ();
+
 	/// <summary>
-	/// Prepare the driver and set the key and mouse events handlers.
+	/// Ends the execution of the console driver.
 	/// </summary>
-	/// <param name="mainLoop">The main loop.</param>
-	/// <param name="keyHandler">The handler for ProcessKey</param>
-	/// <param name="keyDownHandler">The handler for key down events</param>
-	/// <param name="keyUpHandler">The handler for key up events</param>
-	/// <param name="mouseHandler">The handler for mouse events</param>
-	public abstract void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler);
+	internal abstract void End ();
+
+	#endregion
 
 	/// <summary>
-	/// The handler fired when the terminal is resized.
+	/// The event fired when the terminal is resized.
 	/// </summary>
-	protected Action TerminalResized;
+	public event EventHandler<SizeChangedEventArgs> SizeChanged;
+
+	/// <summary>
+	/// Called when the terminal size changes. Fires the <see cref="SizeChanged"/> event.
+	/// </summary>
+	/// <param name="args"></param>
+	public void OnSizeChanged (SizeChangedEventArgs args) => SizeChanged?.Invoke (this, args);
 
 	/// <summary>
 	/// The number of columns visible in the terminal.
@@ -90,12 +100,6 @@ public abstract class ConsoleDriver {
 	///// </summary>
 	public Cell [,] Contents { get; internal set; }
 
-	/// <summary>
-	/// Initializes the driver
-	/// </summary>
-	/// <param name="terminalResized">Method to invoke when the terminal is resized.</param>
-	public abstract void Init (Action terminalResized);
-
 	/// <summary>
 	/// Gets the column last set by <see cref="Move"/>. <see cref="Col"/> and <see cref="Row"/>
 	/// are used by <see cref="AddRune(Rune)"/> and <see cref="AddStr"/> to determine where to add content.
@@ -411,23 +415,14 @@ public abstract class ConsoleDriver {
 		return prevAttribute;
 	}
 
-	/// <summary>
-	/// Make the attribute for the foreground and background colors.
-	/// </summary>
-	/// <param name="fore">Foreground.</param>
-	/// <param name="back">Background.</param>
-	/// <returns></returns>
-	public virtual Attribute MakeAttribute (Color fore, Color back)
-	{
-		return MakeColor (fore, back);
-	}
-
 	/// <summary>
 	/// Gets the current <see cref="Attribute"/>.
 	/// </summary>
 	/// <returns>The current attribute.</returns>
 	public Attribute GetAttribute () => CurrentAttribute;
-	
+
+	// TODO: This is only overridden by CursesDriver. Once CursesDriver supports 24-bit color, this virtual method can be
+	// removed (and Attribute can lose the platformColor property).
 	/// <summary>
 	/// Makes an <see cref="Attribute"/>.
 	/// </summary>
@@ -445,6 +440,64 @@ public abstract class ConsoleDriver {
 	}
 
 
+	#endregion
+
+	#region Mouse and Keyboard
+
+	/// <summary>
+	/// Event fired after a key has been pressed and released.
+	/// </summary>
+	public event EventHandler<KeyEventEventArgs> KeyPressed;
+
+	/// <summary>
+	/// Called after a key has been pressed and released. Fires the <see cref="KeyPressed"/> event.
+	/// </summary>
+	/// <param name="a"></param>
+	public void OnKeyPressed (KeyEventEventArgs a) => KeyPressed?.Invoke(this, a);
+
+	/// <summary>
+	/// Event fired when a key is released.
+	/// </summary>
+	public event EventHandler<KeyEventEventArgs> KeyUp;
+
+	/// <summary>
+	/// Called when a key is released. Fires the <see cref="KeyUp"/> event.
+	/// </summary>
+	/// <param name="a"></param>
+	public void OnKeyUp (KeyEventEventArgs a) => KeyUp?.Invoke (this, a);
+
+	/// <summary>
+	/// Event fired when a key is pressed.
+	/// </summary>
+	public event EventHandler<KeyEventEventArgs> KeyDown;
+
+	/// <summary>
+	/// Called when a key is pressed. Fires the <see cref="KeyDown"/> event.
+	/// </summary>
+	/// <param name="a"></param>
+	public void OnKeyDown (KeyEventEventArgs a) => KeyDown?.Invoke (this, a);
+	
+	/// <summary>
+	/// Event fired when a mouse event occurs.
+	/// </summary>
+	public event EventHandler<MouseEventEventArgs> MouseEvent;
+
+	/// <summary>
+	/// Called when a mouse event occurs. Fires the <see cref="MouseEvent"/> event.
+	/// </summary>
+	/// <param name="a"></param>
+	public void OnMouseEvent (MouseEventEventArgs a) => MouseEvent?.Invoke (this, a);
+
+	/// <summary>
+	/// Simulates a key press.
+	/// </summary>
+	/// <param name="keyChar">The key character.</param>
+	/// <param name="key">The key.</param>
+	/// <param name="shift">If <see langword="true"/> simulates the Shift key being pressed.</param>
+	/// <param name="alt">If <see langword="true"/> simulates the Alt key being pressed.</param>
+	/// <param name="ctrl">If <see langword="true"/> simulates the Ctrl key being pressed.</param>
+	public abstract void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool ctrl);
+
 	#endregion
 
 	/// <summary>
@@ -479,16 +532,6 @@ public abstract class ConsoleDriver {
 	/// <remarks>This is only implemented in <see cref="CursesDriver"/>.</remarks>
 	public abstract void Suspend ();
 
-	/// <summary>
-	/// Simulates a key press.
-	/// </summary>
-	/// <param name="keyChar">The key character.</param>
-	/// <param name="key">The key.</param>
-	/// <param name="shift">If <see langword="true"/> simulates the Shift key being pressed.</param>
-	/// <param name="alt">If <see langword="true"/> simulates the Alt key being pressed.</param>
-	/// <param name="ctrl">If <see langword="true"/> simulates the Ctrl key being pressed.</param>
-	public abstract void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool ctrl);
-
 	// TODO: Move FillRect to ./Drawing	
 	/// <summary>
 	/// Fills the specified rectangle with the specified rune.
@@ -513,11 +556,6 @@ public abstract class ConsoleDriver {
 	/// <param name="c"></param>
 	public void FillRect (Rect rect, char c) => FillRect (rect, new Rune (c));
 
-	/// <summary>
-	/// Ends the execution of the console driver.
-	/// </summary>
-	public abstract void End ();
-
 	/// <summary>
 	/// Returns the name of the driver and relevant library version information.
 	/// </summary>

+ 111 - 152
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -22,9 +22,92 @@ internal class CursesDriver : ConsoleDriver {
 	CursorVisibility? _currentCursorVisibility = null;
 
 	public override string GetVersionInfo () => $"{Curses.curses_version ()}";
-
+	UnixMainLoop _mainLoopDriver = null;
 	public override bool SupportsTrueColor => false;
 
+	object _processInputToken;
+
+	internal override MainLoop Init ()
+	{
+		_mainLoopDriver = new UnixMainLoop (this);
+		if (!RunningUnitTests) {
+
+			_window = Curses.initscr ();
+			Curses.set_escdelay (10);
+
+			// Ensures that all procedures are performed at some previous closing.
+			Curses.doupdate ();
+
+			// 
+			// We are setting Invisible as default so we could ignore XTerm DECSUSR setting
+			//
+			switch (Curses.curs_set (0)) {
+			case 0:
+				_currentCursorVisibility = _initialCursorVisibility = CursorVisibility.Invisible;
+				break;
+
+			case 1:
+				_currentCursorVisibility = _initialCursorVisibility = CursorVisibility.Underline;
+				Curses.curs_set (1);
+				break;
+
+			case 2:
+				_currentCursorVisibility = _initialCursorVisibility = CursorVisibility.Box;
+				Curses.curs_set (2);
+				break;
+
+			default:
+				_currentCursorVisibility = _initialCursorVisibility = null;
+				break;
+			}
+			if (!Curses.HasColors) {
+				throw new InvalidOperationException ("V2 - This should never happen. File an Issue if it does.");
+			}
+
+			Curses.raw ();
+			Curses.noecho ();
+
+			Curses.Window.Standard.keypad (true);
+
+			Curses.StartColor ();
+			Curses.UseDefaultColors ();
+
+			if (!RunningUnitTests) {
+				Curses.timeout (0);
+			}
+
+			_processInputToken = _mainLoopDriver?.AddWatch (0, UnixMainLoop.Condition.PollIn, x => {
+				ProcessInput ();
+				return true;
+			});
+		}
+
+		CurrentAttribute = new Attribute (ColorName.White, ColorName.Black);
+
+		if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
+			Clipboard = new FakeDriver.FakeClipboard ();
+		} else {
+			if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
+				Clipboard = new MacOSXClipboard ();
+			} else {
+				if (Is_WSL_Platform ()) {
+					Clipboard = new WSLClipboard ();
+				} else {
+					Clipboard = new CursesClipboard ();
+				}
+			}
+		}
+
+		ClearContents ();
+		StartReportingMouseMoves ();
+
+		if (!RunningUnitTests) {
+			Curses.CheckWinChange ();
+			Curses.refresh ();
+		}
+		return new MainLoop (_mainLoopDriver);
+	}
+
 	public override void Move (int col, int row)
 	{
 		base.Move (col, row);
@@ -54,11 +137,11 @@ internal class CursesDriver : ConsoleDriver {
 		UpdateCursor ();
 	}
 
-	private void ProcessWinChange ()
+	internal void ProcessWinChange ()
 	{
 		if (!RunningUnitTests && Curses.CheckWinChange ()) {
 			ClearContents ();
-			TerminalResized?.Invoke ();
+			OnSizeChanged (new SizeChangedEventArgs (new Size (Cols, Rows)));
 		}
 	}
 
@@ -82,25 +165,6 @@ internal class CursesDriver : ConsoleDriver {
 			background: CursesColorNumberToColorName (background));
 	}
 
-	/// <remarks>
-	/// In the CursesDriver, colors are encoded as an int. 
-	/// The foreground color is stored in the most significant 4 bits, 
-	/// and the background color is stored in the least significant 4 bits.
-	/// The Terminal.GUi Color values are converted to curses color encoding before being encoded.
-	/// </remarks>
-	private Attribute MakeColor (ColorName foregroundName, ColorName backgroundName)
-	{
-		if (!RunningUnitTests) {
-			return MakeColor (ColorNameToCursesColorNumber (foregroundName), ColorNameToCursesColorNumber (backgroundName));
-		} else {
-			return new Attribute (
-				platformColor: 0,
-				foreground: ColorNameToCursesColorNumber (foregroundName),
-				background: ColorNameToCursesColorNumber (backgroundName));
-		}
-	}
-
-
 	/// <remarks>
 	/// In the CursesDriver, colors are encoded as an int. 
 	/// The foreground color is stored in the most significant 4 bits, 
@@ -110,7 +174,7 @@ internal class CursesDriver : ConsoleDriver {
 	public override Attribute MakeColor (Color foreground, Color background)
 	{
 		if (!RunningUnitTests) {
-			return MakeColor (foreground.ColorName, background.ColorName);
+			return MakeColor (ColorNameToCursesColorNumber (foreground.ColorName), ColorNameToCursesColorNumber (background.ColorName));
 		} else {
 			return new Attribute (
 				platformColor: 0,
@@ -208,14 +272,13 @@ internal class CursesDriver : ConsoleDriver {
 		}
 	}
 
-	public override void End ()
+	internal override void End ()
 	{
 		StopReportingMouseMoves ();
 		SetCursorVisibility (CursorVisibility.Default);
 
-		if (_mainLoop != null) {
-			_mainLoop.RemoveWatch (_processInputToken);
-			_mainLoop.WinChanged -= ProcessInput;
+		if (_mainLoopDriver != null) {
+			_mainLoopDriver.RemoveWatch (_processInputToken);
 		}
 
 		if (RunningUnitTests) {
@@ -371,7 +434,7 @@ internal class CursesDriver : ConsoleDriver {
 		return _keyModifiers;
 	}
 
-	void ProcessInput ()
+	internal void ProcessInput ()
 	{
 		int wch;
 		var code = Curses.get_wch (out wch);
@@ -428,9 +491,9 @@ internal class CursesDriver : ConsoleDriver {
 				wch -= 60;
 				k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch);
 			}
-			_keyDownHandler (new KeyEvent (k, MapKeyModifiers (k)));
-			_keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
-			_keyUpHandler (new KeyEvent (k, MapKeyModifiers (k)));
+			OnKeyDown (new KeyEventEventArgs (new KeyEvent (k, MapKeyModifiers (k))));
+			OnKeyUp (new KeyEventEventArgs (new KeyEvent (k, MapKeyModifiers (k))));
+			OnKeyPressed (new KeyEventEventArgs (new KeyEvent (k, MapKeyModifiers (k))));
 			return;
 		}
 
@@ -481,16 +544,18 @@ internal class CursesDriver : ConsoleDriver {
 					}
 				}
 				key = new KeyEvent (k, MapKeyModifiers (k));
-				_keyDownHandler (key);
-				_keyHandler (key);
+				OnKeyDown (new KeyEventEventArgs (key));
+				OnKeyUp (new KeyEventEventArgs (key));
+				OnKeyPressed (new KeyEventEventArgs (key));
 			} else {
 				k = Key.Esc;
-				_keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
+				OnKeyPressed (new KeyEventEventArgs (new KeyEvent (k, MapKeyModifiers (k))));
 			}
 		} else if (wch == Curses.KeyTab) {
 			k = MapCursesKey (wch);
-			_keyDownHandler (new KeyEvent (k, MapKeyModifiers (k)));
-			_keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
+			OnKeyDown (new KeyEventEventArgs (new KeyEvent (k, MapKeyModifiers (k))));
+			OnKeyUp (new KeyEventEventArgs (new KeyEvent (k, MapKeyModifiers (k))));
+			OnKeyPressed (new KeyEventEventArgs (new KeyEvent (k, MapKeyModifiers (k))));
 		} else {
 			// Unfortunately there are no way to differentiate Ctrl+alfa and Ctrl+Shift+alfa.
 			k = (Key)wch;
@@ -503,9 +568,9 @@ internal class CursesDriver : ConsoleDriver {
 			} else if (wch >= (uint)Key.A && wch <= (uint)Key.Z) {
 				_keyModifiers.Shift = true;
 			}
-			_keyDownHandler (new KeyEvent (k, MapKeyModifiers (k)));
-			_keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
-			_keyUpHandler (new KeyEvent (k, MapKeyModifiers (k)));
+			OnKeyDown (new KeyEventEventArgs (new KeyEvent (k, MapKeyModifiers (k))));
+			OnKeyUp (new KeyEventEventArgs (new KeyEvent (k, MapKeyModifiers (k))));
+			OnKeyPressed (new KeyEventEventArgs (new KeyEvent (k, MapKeyModifiers (k))));
 		}
 		// Cause OnKeyUp and OnKeyPressed. Note that the special handling for ESC above 
 		// will not impact KeyUp.
@@ -525,7 +590,7 @@ internal class CursesDriver : ConsoleDriver {
 			code = Curses.get_wch (out wch2);
 			var consoleKeyInfo = new ConsoleKeyInfo ((char)wch2, 0, false, false, false);
 			if (wch2 == 0 || wch2 == 27 || wch2 == Curses.KeyMouse) {
-				EscSeqUtils.DecodeEscSeq (null, ref consoleKeyInfo, ref ck, cki, ref mod, out _, out _, out _, out _, out bool isKeyMouse, out List<MouseFlags> mouseFlags, out Point pos, out _, ProcessContinuousButtonPressed);
+				EscSeqUtils.DecodeEscSeq (null, ref consoleKeyInfo, ref ck, cki, ref mod, out _, out _, out _, out _, out bool isKeyMouse, out List<MouseFlags> mouseFlags, out Point pos, out _, ProcessMouseEvent);
 				if (isKeyMouse) {
 					foreach (var mf in mouseFlags) {
 						ProcessMouseEvent (mf, pos);
@@ -539,8 +604,8 @@ internal class CursesDriver : ConsoleDriver {
 					k = ConsoleKeyMapping.MapConsoleKeyToKey (consoleKeyInfo.Key, out _);
 					k = ConsoleKeyMapping.MapKeyModifiers (consoleKeyInfo, k);
 					key = new KeyEvent (k, MapKeyModifiers (k));
-					_keyDownHandler (key);
-					_keyHandler (key);
+					OnKeyDown (new KeyEventEventArgs (key));
+					OnKeyPressed (new KeyEventEventArgs (key));
 				}
 			} else {
 				cki = EscSeqUtils.ResizeArray (consoleKeyInfo, cki);
@@ -592,115 +657,9 @@ internal class CursesDriver : ConsoleDriver {
 			X = pos.X,
 			Y = pos.Y
 		};
-		_mouseHandler (me);
-	}
-
-
-	void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos)
-	{
-		ProcessMouseEvent (mouseFlag, pos);
-	}
-
-	Action<KeyEvent> _keyHandler;
-	Action<KeyEvent> _keyDownHandler;
-	Action<KeyEvent> _keyUpHandler;
-	Action<MouseEvent> _mouseHandler;
-
-	UnixMainLoop _mainLoop;
-	object _processInputToken;
-
-	public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
-	{
-		if (!RunningUnitTests) {
-			// Note: Curses doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called
-			Curses.timeout (0);
-		}
-		this._keyHandler = keyHandler;
-		this._keyDownHandler = keyDownHandler;
-		this._keyUpHandler = keyUpHandler;
-		this._mouseHandler = mouseHandler;
-
-		_mainLoop = mainLoop.MainLoopDriver as UnixMainLoop;
-
-		_processInputToken = _mainLoop?.AddWatch (0, UnixMainLoop.Condition.PollIn, x => {
-			ProcessInput ();
-			return true;
-		});
-
-		_mainLoop.WinChanged = ProcessInput;
+		OnMouseEvent (new MouseEventEventArgs (me));
 	}
 
-	public override void Init (Action terminalResized)
-	{
-		if (!RunningUnitTests) {
-
-			_window = Curses.initscr ();
-			Curses.set_escdelay (10);
-
-			// Ensures that all procedures are performed at some previous closing.
-			Curses.doupdate ();
-
-			// 
-			// We are setting Invisible as default so we could ignore XTerm DECSUSR setting
-			//
-			switch (Curses.curs_set (0)) {
-			case 0:
-				_currentCursorVisibility = _initialCursorVisibility = CursorVisibility.Invisible;
-				break;
-
-			case 1:
-				_currentCursorVisibility = _initialCursorVisibility = CursorVisibility.Underline;
-				Curses.curs_set (1);
-				break;
-
-			case 2:
-				_currentCursorVisibility = _initialCursorVisibility = CursorVisibility.Box;
-				Curses.curs_set (2);
-				break;
-
-			default:
-				_currentCursorVisibility = _initialCursorVisibility = null;
-				break;
-			}
-			if (!Curses.HasColors) {
-				throw new InvalidOperationException ("V2 - This should never happen. File an Issue if it does.");
-			}
-
-			Curses.raw ();
-			Curses.noecho ();
-
-			Curses.Window.Standard.keypad (true);
-
-			Curses.StartColor ();
-			Curses.UseDefaultColors ();
-		}
-
-		CurrentAttribute = MakeColor (ColorName.White, ColorName.Black);
-
-		TerminalResized = terminalResized;
-
-		if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
-			Clipboard = new FakeDriver.FakeClipboard ();
-		} else {
-			if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
-				Clipboard = new MacOSXClipboard ();
-			} else {
-				if (Is_WSL_Platform ()) {
-					Clipboard = new WSLClipboard ();
-				} else {
-					Clipboard = new CursesClipboard ();
-				}
-			}
-		}
-
-		ClearContents ();
-		StartReportingMouseMoves ();
-
-		if (!RunningUnitTests) {
-			Curses.CheckWinChange ();
-			Curses.refresh ();
-		}
-	}
 
 	public static bool Is_WSL_Platform ()
 	{
@@ -819,9 +778,9 @@ internal class CursesDriver : ConsoleDriver {
 			key |= Key.CtrlMask;
 			km.Ctrl = control;
 		}
-		_keyDownHandler (new KeyEvent (key, km));
-		_keyHandler (new KeyEvent (key, km));
-		_keyUpHandler (new KeyEvent (key, km));
+		OnKeyDown (new KeyEventEventArgs (new KeyEvent (key, km)));
+		OnKeyPressed (new KeyEventEventArgs (new KeyEvent (key, km)));
+		OnKeyUp (new KeyEventEventArgs (new KeyEvent (key, km)));
 	}
 
 

+ 7 - 5
Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs

@@ -15,9 +15,11 @@ namespace Terminal.Gui {
 	/// can watch file descriptors using the AddWatch methods.
 	/// </remarks>
 	internal class UnixMainLoop : IMainLoopDriver {
+		private CursesDriver _cursesDriver;
 		public UnixMainLoop (ConsoleDriver consoleDriver = null)
 		{
 			// UnixDriver doesn't use the consoleDriver parameter, but the WindowsDriver does.
+			_cursesDriver = (CursesDriver)Application.Driver;
 		}
 
 		public const int KEY_RESIZE = unchecked((int)0xffffffffffffffff);
@@ -86,8 +88,6 @@ namespace Terminal.Gui {
 		MainLoop _mainLoop;
 		bool _winChanged;
 
-		internal Action WinChanged;
-
 		void IMainLoopDriver.Wakeup ()
 		{
 			if (!ConsoleDriver.RunningUnitTests) {
@@ -119,7 +119,7 @@ namespace Terminal.Gui {
 		/// <remarks>
 		///	The token parameter is the value returned from AddWatch
 		/// </remarks>
-		public void RemoveWatch (object token)
+		internal void RemoveWatch (object token)
 		{
 			if (!ConsoleDriver.RunningUnitTests) {
 				if (token is not Watch watch) {
@@ -140,7 +140,7 @@ namespace Terminal.Gui {
 		///  The return value is a token that represents this watch, you can
 		///  use this token to remove the watch by calling RemoveWatch.
 		/// </remarks>
-		public object AddWatch (int fileDescriptor, Condition condition, Func<MainLoop, bool> callback)
+		internal object AddWatch (int fileDescriptor, Condition condition, Func<MainLoop, bool> callback)
 		{
 			if (callback == null) {
 				throw new ArgumentNullException (nameof (callback));
@@ -187,7 +187,9 @@ namespace Terminal.Gui {
 		{
 			if (_winChanged) {
 				_winChanged = false;
-				WinChanged?.Invoke ();
+				_cursesDriver.ProcessInput ();
+				// This is needed on the mac. See https://github.com/gui-cs/Terminal.Gui/pull/2922#discussion_r1365992426
+				_cursesDriver.ProcessWinChange ();
 			}
 			if (_pollMap == null) return;
 			foreach (var p in _pollMap) {

+ 23 - 2
Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs

@@ -5,6 +5,7 @@ using System.Linq;
 using System.Management;
 using System.Runtime.InteropServices;
 using System.Threading.Tasks;
+using static Unix.Terminal.Curses;
 
 namespace Terminal.Gui;
 /// <summary>
@@ -72,22 +73,42 @@ public static class EscSeqUtils {
 	/// <summary>
 	/// ESC [ ? 1047 h - Activate xterm alternative buffer (no backscroll)
 	/// </summary>
+	/// <remarks>
+	/// From https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_
+	/// Use Alternate Screen Buffer, xterm. 
+	/// </remarks>
 	public static readonly string CSI_ActivateAltBufferNoBackscroll = CSI + "?1047h";
 
 	/// <summary>
 	/// ESC [ ? 1047 l - Restore xterm working buffer (with backscroll)
 	/// </summary>
+	/// <remarks>
+	/// From https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_
+	/// Use Normal Screen Buffer, xterm.  Clear the screen first if in the Alternate Screen Buffer.
+	/// </remarks>
 	public static readonly string CSI_RestoreAltBufferWithBackscroll = CSI + "?1047l";
 
 	/// <summary>
 	/// ESC [ ? 1049 h - Save cursor position and activate xterm alternative buffer (no backscroll)
 	/// </summary>
+	/// <remarks>
+	/// From https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_
+	/// Save cursor as in DECSC, xterm. After saving the cursor, switch to the Alternate Screen Buffer,
+	/// clearing it first.
+	/// This control combines the effects of the 1047 and 1048 modes.
+	/// Use this with terminfo-based applications rather than the 47 mode.
+	/// </remarks>
 	public static readonly string CSI_SaveCursorAndActivateAltBufferNoBackscroll = CSI + "?1049h";
 
 	/// <summary>
 	/// ESC [ ? 1049 l - Restore cursor position and restore xterm working buffer (with backscroll)
 	/// </summary>
-	public static readonly string CSI_RestoreCursorAndActivateAltBufferWithBackscroll = CSI + "?1049l";
+	/// <remarks>
+	/// From https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_
+	/// Use Normal Screen Buffer and restore cursor as in DECRC, xterm.
+	/// resource.This combines the effects of the 1047 and 1048  modes.
+	/// </remarks>
+	public static readonly string CSI_RestoreCursorAndRestoreAltBufferWithBackscroll = CSI + "?1049l";
 
 	/// <summary>
 	/// Options for ANSI ESC "[xJ" - Clears part of the screen.
@@ -1060,7 +1081,7 @@ public static class EscSeqUtils {
 			if (view == null)
 				break;
 			if (isButtonPressed && lastMouseButtonPressed != null && (mouseFlag & MouseFlags.ReportMousePosition) == 0) {
-				Application.MainLoop.Invoke (() => continuousButtonPressedHandler (mouseFlag, point));
+				Application.Invoke (() => continuousButtonPressedHandler (mouseFlag, point));
 			}
 		}
 	}

+ 14 - 22
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs

@@ -57,24 +57,28 @@ public class FakeDriver : ConsoleDriver {
 		}
 	}
 
-	public override void End ()
+	internal override void End ()
 	{
 		FakeConsole.ResetColor ();
 		FakeConsole.Clear ();
 	}
 
-	public override void Init (Action terminalResized)
+	FakeMainLoop _mainLoopDriver = null;
+
+	internal override MainLoop Init ()
 	{
 		FakeConsole.MockKeyPresses.Clear ();
 
-		TerminalResized = terminalResized;
-
 		Cols = FakeConsole.WindowWidth = FakeConsole.BufferWidth = FakeConsole.WIDTH;
 		Rows = FakeConsole.WindowHeight = FakeConsole.BufferHeight = FakeConsole.HEIGHT;
 		FakeConsole.Clear ();
 		ResizeScreen ();
 		CurrentAttribute = new Attribute (Color.White, Color.Black);
 		ClearContents ();
+		
+		_mainLoopDriver = new FakeMainLoop (this);
+		_mainLoopDriver.KeyPressed = ProcessInput;
+		return new MainLoop (_mainLoopDriver);
 	}
 
 
@@ -341,20 +345,8 @@ public class FakeDriver : ConsoleDriver {
 		return keyMod != Key.Null ? keyMod | key : key;
 	}
 
-	Action<KeyEvent> _keyDownHandler;
-	Action<KeyEvent> _keyHandler;
-	Action<KeyEvent> _keyUpHandler;
 	private CursorVisibility _savedCursorVisibility;
 
-	public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
-	{
-		_keyDownHandler = keyDownHandler;
-		_keyHandler = keyHandler;
-		_keyUpHandler = keyUpHandler;
-
-		// Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called
-		(mainLoop.MainLoopDriver as FakeMainLoop).KeyPressed = (consoleKey) => ProcessInput (consoleKey);
-	}
 
 	void ProcessInput (ConsoleKeyInfo consoleKey)
 	{
@@ -374,15 +366,15 @@ public class FakeDriver : ConsoleDriver {
 		var map = MapKey (consoleKey);
 		if (map == (Key)0xffffffff) {
 			if ((consoleKey.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
-				_keyDownHandler (new KeyEvent (map, keyModifiers));
-				_keyUpHandler (new KeyEvent (map, keyModifiers));
+				OnKeyDown(new KeyEventEventArgs(new KeyEvent (map, keyModifiers)));
+				OnKeyUp (new KeyEventEventArgs (new KeyEvent (map, keyModifiers)));
 			}
 			return;
 		}
 
-		_keyDownHandler (new KeyEvent (map, keyModifiers));
-		_keyHandler (new KeyEvent (map, keyModifiers));
-		_keyUpHandler (new KeyEvent (map, keyModifiers));
+		OnKeyDown (new KeyEventEventArgs (new KeyEvent (map, keyModifiers)));
+		OnKeyUp (new KeyEventEventArgs (new KeyEvent (map, keyModifiers)));
+		OnKeyPressed (new KeyEventEventArgs (new KeyEvent (map, keyModifiers)));
 	}
 
 	/// <inheritdoc/>
@@ -454,7 +446,7 @@ public class FakeDriver : ConsoleDriver {
 	{
 		ResizeScreen ();
 		ClearContents ();
-		TerminalResized?.Invoke ();
+		OnSizeChanged (new SizeChangedEventArgs (new Size (Cols, Rows)));
 	}
 
 	public virtual void ResizeScreen ()

+ 34 - 50
Terminal.Gui/ConsoleDrivers/NetDriver.cs

@@ -628,32 +628,14 @@ internal class NetDriver : ConsoleDriver {
 	const int COLOR_BRIGHT_CYAN = 96;
 	const int COLOR_BRIGHT_WHITE = 97;
 
+	NetMainLoop _mainLoopDriver = null;
+
 	public override bool SupportsTrueColor => Environment.OSVersion.Platform == PlatformID.Unix || (IsWinPlatform && Environment.OSVersion.Version.Build >= 14931);
 
 	public NetWinVTConsole NetWinConsole { get; private set; }
 	public bool IsWinPlatform { get; private set; }
 
-	public override void End ()
-	{
-		if (IsWinPlatform) {
-			NetWinConsole?.Cleanup ();
-		}
-
-		StopReportingMouseMoves ();
-
-		if (!RunningUnitTests) {
-			Console.ResetColor ();
-
-			//Disable alternative screen buffer.
-			Console.Out.Write (EscSeqUtils.CSI_RestoreCursorAndActivateAltBufferWithBackscroll);
-
-			//Set cursor key to cursor.
-			Console.Out.Write (EscSeqUtils.CSI_ShowCursor);
-			Console.Out.Close ();
-		}
-	}
-
-	public override void Init (Action terminalResized)
+	internal override MainLoop Init ()
 	{
 		var p = Environment.OSVersion.Platform;
 		if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
@@ -676,9 +658,6 @@ internal class NetDriver : ConsoleDriver {
 			}
 		}
 
-		TerminalResized = terminalResized;
-
-
 		if (!RunningUnitTests) {
 			Console.TreatControlCAsInput = true;
 
@@ -703,6 +682,30 @@ internal class NetDriver : ConsoleDriver {
 		CurrentAttribute = new Attribute (Color.White, Color.Black);
 
 		StartReportingMouseMoves ();
+
+		_mainLoopDriver = new NetMainLoop (this);
+		_mainLoopDriver.ProcessInput = ProcessInput;
+		return new MainLoop (_mainLoopDriver);
+	}
+
+	internal override void End ()
+	{
+		if (IsWinPlatform) {
+			NetWinConsole?.Cleanup ();
+		}
+
+		StopReportingMouseMoves ();
+
+		if (!RunningUnitTests) {
+			Console.ResetColor ();
+
+			//Disable alternative screen buffer.
+			Console.Out.Write (EscSeqUtils.CSI_RestoreCursorAndRestoreAltBufferWithBackscroll);
+
+			//Set cursor key to cursor.
+			Console.Out.Write (EscSeqUtils.CSI_ShowCursor);
+			Console.Out.Close ();
+		}
 	}
 
 	public virtual void ResizeScreen ()
@@ -1106,25 +1109,6 @@ internal class NetDriver : ConsoleDriver {
 		return keyMod != Key.Null ? keyMod | key : key;
 	}
 
-	Action<KeyEvent> _keyHandler;
-	Action<KeyEvent> _keyDownHandler;
-	Action<KeyEvent> _keyUpHandler;
-	Action<MouseEvent> _mouseHandler;
-	NetMainLoop _mainLoop;
-
-	public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
-	{
-		_keyHandler = keyHandler;
-		_keyDownHandler = keyDownHandler;
-		_keyUpHandler = keyUpHandler;
-		_mouseHandler = mouseHandler;
-
-		_mainLoop = mainLoop.MainLoopDriver as NetMainLoop;
-
-		// Note: .Net API doesn't support keydown/up events and thus any passed keyDown/UpHandlers will be simulated to be called.
-		_mainLoop.ProcessInput = ProcessInput;
-	}
-
 	volatile bool _winSizeChanging;
 
 	void ProcessInput (NetEvents.InputResult inputEvent)
@@ -1141,16 +1125,16 @@ internal class NetDriver : ConsoleDriver {
 				return;
 			}
 			if (map == Key.Null) {
-				_keyDownHandler (new KeyEvent (map, _keyModifiers));
-				_keyUpHandler (new KeyEvent (map, _keyModifiers));
+				OnKeyDown (new KeyEventEventArgs (new KeyEvent (map, _keyModifiers)));
+				OnKeyUp (new KeyEventEventArgs (new KeyEvent (map, _keyModifiers)));
 			} else {
-				_keyDownHandler (new KeyEvent (map, _keyModifiers));
-				_keyHandler (new KeyEvent (map, _keyModifiers));
-				_keyUpHandler (new KeyEvent (map, _keyModifiers));
+				OnKeyDown (new KeyEventEventArgs (new KeyEvent (map, _keyModifiers)));
+				OnKeyUp (new KeyEventEventArgs (new KeyEvent (map, _keyModifiers)));
+				OnKeyPressed (new KeyEventEventArgs (new KeyEvent (map, _keyModifiers)));
 			}
 			break;
 		case NetEvents.EventType.Mouse:
-			_mouseHandler (ToDriverMouse (inputEvent.MouseEvent));
+			OnMouseEvent (new MouseEventEventArgs (ToDriverMouse (inputEvent.MouseEvent)));
 			break;
 		case NetEvents.EventType.WindowSize:
 			_winSizeChanging = true;
@@ -1161,7 +1145,7 @@ internal class NetDriver : ConsoleDriver {
 			ResizeScreen ();
 			ClearContents ();
 			_winSizeChanging = false;
-			TerminalResized?.Invoke ();
+			OnSizeChanged (new SizeChangedEventArgs (new Size (Cols, Rows)));
 			break;
 		case NetEvents.EventType.RequestResponse:
 			// BUGBUG: What is this for? It does not seem to be used anywhere. 

+ 79 - 157
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -18,12 +18,10 @@ using System.Text;
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
-using System.Linq;
 using System.Runtime.InteropServices;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Diagnostics;
-using System.Management;
 
 namespace Terminal.Gui;
 
@@ -780,18 +778,13 @@ internal class WindowsConsole {
 internal class WindowsDriver : ConsoleDriver {
 	WindowsConsole.ExtendedCharInfo [] _outputBuffer;
 	WindowsConsole.SmallRect _damageRegion;
-	Action<KeyEvent> _keyHandler;
-	Action<KeyEvent> _keyDownHandler;
-	Action<KeyEvent> _keyUpHandler;
-	Action<MouseEvent> _mouseHandler;
 
 	public WindowsConsole WinConsole { get; private set; }
 
-	public override bool SupportsTrueColor => RunningUnitTests || (Environment.OSVersion.Version.Build >= 14931
-		&& (_isWindowsTerminal || _parentProcessName == "devenv"));
+	public override bool SupportsTrueColor => RunningUnitTests || (Environment.OSVersion.Version.Build >= 14931 && _isWindowsTerminal);
 
 	readonly bool _isWindowsTerminal = false;
-	readonly string _parentProcessName = "WindowsTerminal";
+	WindowsMainLoop _mainLoopDriver = null;
 
 	public WindowsDriver ()
 	{
@@ -803,68 +796,59 @@ internal class WindowsDriver : ConsoleDriver {
 			Clipboard = new FakeDriver.FakeClipboard ();
 		}
 
-		if (!RunningUnitTests) {
-			_parentProcessName = GetParentProcessName ();
-			_isWindowsTerminal = _parentProcessName == "WindowsTerminal";
-			if (!_isWindowsTerminal && _parentProcessName != "devenv") {
-				Force16Colors = true;
-			}
+		// TODO: if some other Windows-based terminal supports true color, update this logic to not
+		// force 16color mode (.e.g ConEmu which really doesn't work well at all).
+		_isWindowsTerminal = Environment.GetEnvironmentVariable ("WT_SESSION") != null;
+		if (!_isWindowsTerminal) {
+			Force16Colors = true;
 		}
 	}
 
-	private static string GetParentProcessName ()
+	internal override MainLoop Init ()
 	{
-#pragma warning disable CA1416 // Validate platform compatibility
-		var myId = Process.GetCurrentProcess ().Id;
-		var query = string.Format ($"SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = {myId}");
-		var search = new ManagementObjectSearcher ("root\\CIMV2", query);
-		var queryObj = search.Get ().OfType<ManagementBaseObject> ().FirstOrDefault ();
-		if (queryObj == null) {
-			return null;
+		_mainLoopDriver = new WindowsMainLoop (this);
+		if (RunningUnitTests) {
+			return new MainLoop (_mainLoopDriver);
 		}
-		var parentId = (uint)queryObj ["ParentProcessId"];
-		var parent = Process.GetProcessById ((int)parentId);
-		var prevParent = parent;
-
-		// Check if the parent is from other parent
-		while (queryObj != null) {
-			query = string.Format ($"SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = {parentId}");
-			search = new ManagementObjectSearcher ("root\\CIMV2", query);
-			queryObj = search.Get ().OfType<ManagementBaseObject> ().FirstOrDefault ();
-			if (queryObj == null) {
-				return parent.ProcessName;
+
+		try {
+			if (WinConsole != null) {
+				// BUGBUG: The results from GetConsoleOutputWindow are incorrect when called from Init. 
+				// Our thread in WindowsMainLoop.CheckWin will get the correct results. See #if HACK_CHECK_WINCHANGED
+				var winSize = WinConsole.GetConsoleOutputWindow (out Point pos);
+				Cols = winSize.Width;
+				Rows = winSize.Height;
 			}
-			parentId = (uint)queryObj ["ParentProcessId"];
-			try {
-				parent = Process.GetProcessById ((int)parentId);
-				if (string.Equals (parent.ProcessName, "explorer", StringComparison.InvariantCultureIgnoreCase)) {
-					return prevParent.ProcessName;
-				}
-				prevParent = parent;
-			} catch (ArgumentException) {
+			WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion);
 
-				return prevParent.ProcessName;
+			if (_isWindowsTerminal) {
+				Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll);
 			}
+		} catch (Win32Exception e) {
+			// We are being run in an environment that does not support a console
+			// such as a unit test, or a pipe.
+			Debug.WriteLine ($"Likely running unit tests. Setting WinConsole to null so we can test it elsewhere. Exception: {e}");
+			WinConsole = null;
 		}
 
-		return parent.ProcessName;
-#pragma warning restore CA1416 // Validate platform compatibility
-	}
+		CurrentAttribute = new Attribute (Color.White, Color.Black);
 
-	WindowsMainLoop _mainLoop;
+		_outputBuffer = new WindowsConsole.ExtendedCharInfo [Rows * Cols];
+		Clip = new Rect (0, 0, Cols, Rows);
+		_damageRegion = new WindowsConsole.SmallRect () {
+			Top = 0,
+			Left = 0,
+			Bottom = (short)Rows,
+			Right = (short)Cols
+		};
 
-	public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
-	{
-		_keyHandler = keyHandler;
-		_keyDownHandler = keyDownHandler;
-		_keyUpHandler = keyUpHandler;
-		_mouseHandler = mouseHandler;
+		ClearContents ();
 
-		_mainLoop = mainLoop.MainLoopDriver as WindowsMainLoop;
-		_mainLoop.ProcessInput = ProcessInput;
 #if HACK_CHECK_WINCHANGED
-		_mainLoop.WinChanged = ChangeWin;
+		_mainLoopDriver.WinChanged = ChangeWin;
 #endif
+		return new MainLoop (_mainLoopDriver);
+
 	}
 
 #if HACK_CHECK_WINCHANGED
@@ -889,11 +873,20 @@ internal class WindowsDriver : ConsoleDriver {
 
 		ResizeScreen ();
 		ClearContents ();
-		TerminalResized.Invoke ();
+		OnSizeChanged (new SizeChangedEventArgs (new Size (Cols, Rows)));
 	}
 #endif
 
-	void ProcessInput (WindowsConsole.InputRecord inputEvent)
+	// This is a bit hacky, but it enables users to hold down a key and 
+	// OnKeyDown, OnKeyPressed, OnKeyPressed, OnKeyUp
+	// It might be worth making OnKeyDown and OnKeyUp virtual so this can be tracked from those calls in case
+	// somoene calls them externally??
+	//
+	// It also is broken when modifiers keys are down too
+	//
+	//Key _keyDown = (Key)0xffffffff;
+	
+	internal void ProcessInput (WindowsConsole.InputRecord inputEvent)
 	{
 		switch (inputEvent.EventType) {
 		case WindowsConsole.EventType.Key:
@@ -971,19 +964,26 @@ internal class WindowsDriver : ConsoleDriver {
 				}
 
 				if (inputEvent.KeyEvent.bKeyDown) {
-					_keyDownHandler (key);
+					//_keyDown = key.Key;
+					OnKeyDown (new KeyEventEventArgs (key));
 				} else {
-					_keyUpHandler (key);
+					//_keyDown = (Key)0xffffffff;
+					OnKeyUp (new KeyEventEventArgs (key));
 				}
 			} else {
 				if (inputEvent.KeyEvent.bKeyDown) {
 					// May occurs using SendKeys
 					_keyModifiers ??= new KeyModifiers ();
-					// Key Down - Fire KeyDown Event and KeyStroke (ProcessKey) Event
-					_keyDownHandler (new KeyEvent (map, _keyModifiers));
-					_keyHandler (new KeyEvent (map, _keyModifiers));
+
+					//if (_keyDown == (Key)0xffffffff) {
+					       // Avoid sending repeat keydowns
+					//	_keyDown = map;
+						OnKeyDown (new KeyEventEventArgs (new KeyEvent (map, _keyModifiers)));
+					//}
+					OnKeyPressed (new KeyEventEventArgs (new KeyEvent (map, _keyModifiers)));
 				} else {
-					_keyUpHandler (new KeyEvent (map, _keyModifiers));
+					//_keyDown = (Key)0xffffffff;
+					OnKeyUp (new KeyEventEventArgs (new KeyEvent (map, _keyModifiers)));
 				}
 			}
 			if (!inputEvent.KeyEvent.bKeyDown && inputEvent.KeyEvent.dwControlKeyState == 0) {
@@ -993,14 +993,13 @@ internal class WindowsDriver : ConsoleDriver {
 
 		case WindowsConsole.EventType.Mouse:
 			var me = ToDriverMouse (inputEvent.MouseEvent);
-			_mouseHandler (me);
+			OnMouseEvent (new MouseEventEventArgs (me));
 			if (_processButtonClick) {
-				_mouseHandler (
-				    new MouseEvent () {
-					    X = me.X,
-					    Y = me.Y,
-					    Flags = ProcessButtonClick (inputEvent.MouseEvent)
-				    });
+				OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+					X = me.X,
+					Y = me.Y,
+					Flags = ProcessButtonClick (inputEvent.MouseEvent)
+				}));
 			}
 			break;
 
@@ -1258,7 +1257,7 @@ internal class WindowsDriver : ConsoleDriver {
 				break;
 			}
 			if (_isButtonPressed && (mouseFlag & MouseFlags.ReportMousePosition) == 0) {
-				Application.MainLoop.Invoke (() => _mouseHandler (me));
+				Application.Invoke (() => OnMouseEvent (new MouseEventEventArgs (me)));
 			}
 		}
 	}
@@ -1505,57 +1504,6 @@ internal class WindowsDriver : ConsoleDriver {
 		return base.IsRuneSupported (rune) && rune.IsBmp;
 	}
 
-	public override void Init (Action terminalResized)
-	{
-		TerminalResized = terminalResized;
-
-		if (RunningUnitTests) {
-			return;
-		}
-
-		try {
-			if (WinConsole != null) {
-				// BUGBUG: The results from GetConsoleOutputWindow are incorrect when called from Init. 
-				// Our thread in WindowsMainLoop.CheckWin will get the correct results. See #if HACK_CHECK_WINCHANGED
-				var winSize = WinConsole.GetConsoleOutputWindow (out Point pos);
-				Cols = winSize.Width;
-				Rows = winSize.Height;
-			}
-			WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion);
-
-			// Needed for Windows Terminal
-			// ESC [ ? 1047 h  Save cursor position and activate xterm alternative buffer (no backscroll)
-			// ESC [ ? 1047 l  Restore cursor position and restore xterm working buffer (with backscroll)
-			// ESC [ ? 1048 h  Save cursor position
-			// ESC [ ? 1048 l  Restore cursor position
-			// ESC [ ? 1049 h  Activate xterm alternative buffer (no backscroll)
-			// ESC [ ? 1049 l  Restore xterm working buffer (with backscroll)
-			// Per Issue #2264 using the alternative screen buffer is required for Windows Terminal to not 
-			// wipe out the backscroll buffer when the application exits.
-			if (_isWindowsTerminal) {
-				Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll);
-			}
-		} catch (Win32Exception e) {
-			// We are being run in an environment that does not support a console
-			// such as a unit test, or a pipe.
-			Debug.WriteLine ($"Likely running unit tests. Setting WinConsole to null so we can test it elsewhere. Exception: {e}");
-			WinConsole = null;
-		}
-
-		CurrentAttribute = new Attribute (Color.White, Color.Black);
-
-		_outputBuffer = new WindowsConsole.ExtendedCharInfo [Rows * Cols];
-		Clip = new Rect (0, 0, Cols, Rows);
-		_damageRegion = new WindowsConsole.SmallRect () {
-			Top = 0,
-			Left = 0,
-			Bottom = (short)Rows,
-			Right = (short)Cols
-		};
-
-		ClearContents ();
-	}
-
 	void ResizeScreen ()
 	{
 		_outputBuffer = new WindowsConsole.ExtendedCharInfo [Rows * Cols];
@@ -1573,7 +1521,7 @@ internal class WindowsDriver : ConsoleDriver {
 
 	public override void UpdateScreen ()
 	{
-		var windowSize = WinConsole?.GetConsoleBufferWindow (out _) ?? new Size (Cols, Rows);
+		var windowSize = WinConsole?.GetConsoleBufferWindow (out var _) ?? new Size (Cols, Rows);
 		if (!windowSize.IsEmpty && (windowSize.Width != Cols || windowSize.Height != Rows)) {
 			return;
 		}
@@ -1637,25 +1585,6 @@ internal class WindowsDriver : ConsoleDriver {
 		UpdateCursor ();
 	}
 
-	#region Color Handling
-
-	/// <summary>
-	/// In the WindowsDriver, colors are encoded as an int. 
-	/// The background color is stored in the least significant 4 bits, 
-	/// and the foreground color is stored in the next 4 bits. 
-	/// </summary>
-	public override Attribute MakeColor (Color foreground, Color background)
-	{
-		// Encode the colors into the int value.
-		return new Attribute (
-			platformColor: 0, // Not used anymore! (((int)foreground.ColorName) | ((int)background.ColorName << 4)),
-			foreground: foreground,
-			background: background
-		);
-	}
-
-	#endregion
-
 	CursorVisibility _cachedCursorVisibility;
 
 	public override void UpdateCursor ()
@@ -1751,22 +1680,21 @@ internal class WindowsDriver : ConsoleDriver {
 		}
 	}
 
-	public override void End ()
+	internal override void End ()
 	{
-		if (_mainLoop != null) {
-			_mainLoop.ProcessInput -= ProcessInput;
+		if (_mainLoopDriver != null) {
 #if HACK_CHECK_WINCHANGED
 			//_mainLoop.WinChanged -= ChangeWin;
 #endif
 		}
-		_mainLoop = null;
+		_mainLoopDriver = null;
 
 		WinConsole?.Cleanup ();
 		WinConsole = null;
 
-		if (!RunningUnitTests && (_isWindowsTerminal || _parentProcessName == "devenv")) {
+		if (!RunningUnitTests && _isWindowsTerminal) {
 			// Disable alternative screen buffer.
-			Console.Out.Write (EscSeqUtils.CSI_RestoreCursorAndActivateAltBufferWithBackscroll);
+			Console.Out.Write (EscSeqUtils.CSI_RestoreCursorAndRestoreAltBufferWithBackscroll);
 		}
 	}
 
@@ -1797,11 +1725,6 @@ internal class WindowsMainLoop : IMainLoopDriver {
 	// The records that we keep fetching
 	readonly Queue<WindowsConsole.InputRecord []> _resultQueue = new Queue<WindowsConsole.InputRecord []> ();
 
-	/// <summary>
-	/// Invoked when a Key is pressed or released.
-	/// </summary>
-	public Action<WindowsConsole.InputRecord> ProcessInput;
-
 	/// <summary>
 	/// Invoked when the window is changed.
 	/// </summary>
@@ -1879,7 +1802,7 @@ internal class WindowsMainLoop : IMainLoopDriver {
 	bool IMainLoopDriver.EventsPending ()
 	{
 		_waitForProbe.Set ();
-#if HACK_CHECK_WINCHANGED          
+#if HACK_CHECK_WINCHANGED
 		_winChange.Set ();
 #endif
 		if (_mainLoop.CheckTimersAndIdleHandlers (out var waitTimeout)) {
@@ -1916,8 +1839,7 @@ internal class WindowsMainLoop : IMainLoopDriver {
 		while (_resultQueue.Count > 0) {
 			var inputRecords = _resultQueue.Dequeue ();
 			if (inputRecords is { Length: > 0 }) {
-				var inputEvent = inputRecords [0];
-				ProcessInput?.Invoke (inputEvent);
+				((WindowsDriver)_consoleDriver).ProcessInput (inputRecords [0]);
 			}
 		}
 #if HACK_CHECK_WINCHANGED

+ 2 - 1
Terminal.Gui/Drawing/Color.cs

@@ -706,12 +706,13 @@ public readonly struct Attribute : IEquatable<Attribute> {
 		Foreground = foreground;
 		Background = background;
 
+		// TODO: Once CursesDriver supports truecolor all the PlatformColor stuff goes away
 		if (Application.Driver == null) {
 			PlatformColor = -1;
 			return;
 		}
 
-		var make = Application.Driver.MakeAttribute (foreground, background);
+		var make = Application.Driver.MakeColor (foreground, background);
 		PlatformColor = make.PlatformColor;
 	}
 

+ 3 - 1
Terminal.Gui/Input/Event.cs

@@ -766,12 +766,14 @@ namespace Terminal.Gui {
 		AllEvents = unchecked((int)0x7ffffff),
 	}
 
+	// TODO: Merge MouseEvent and MouseEventEventArgs into a single class.
+
 	/// <summary>
 	/// Low-level construct that conveys the details of mouse events, such
 	/// as coordinates and button state, from ConsoleDrivers up to <see cref="Application"/> and
 	/// Views.
 	/// </summary>
-	/// <remarks>The <see cref="Application"/> class includes the <see cref="Application.RootMouseEvent"/>
+	/// <remarks>The <see cref="Application"/> class includes the <see cref="Application.MouseEvent"/>
 	/// Action which takes a MouseEvent argument.</remarks>
 	public class MouseEvent {
 		/// <summary>

+ 2 - 0
Terminal.Gui/Input/MouseEventEventArgs.cs

@@ -12,6 +12,8 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="me">The mouse event.</param>
 		public MouseEventEventArgs (MouseEvent me) => MouseEvent = me;
+
+		// TODO: Merge MouseEvent and MouseEventEventArgs into a single class.
 		/// <summary>
 		/// The <see cref="Terminal.Gui.MouseEvent"/> for the event.
 		/// </summary>

+ 40 - 43
Terminal.Gui/MainLoop.cs

@@ -12,7 +12,7 @@ namespace Terminal.Gui {
 	/// <summary>
 	/// Public interface to create a platform specific <see cref="MainLoop"/> driver.
 	/// </summary>
-	public interface IMainLoopDriver {
+	internal interface IMainLoopDriver {
 		/// <summary>
 		/// Initializes the <see cref="MainLoop"/>, gets the calling main loop for the initialization.
 		/// </summary>
@@ -44,6 +44,7 @@ namespace Terminal.Gui {
 		void TearDown ();
 	}
 
+
 	/// <summary>
 	///   The MainLoop monitors timers and idle handlers.
 	/// </summary>
@@ -51,7 +52,7 @@ namespace Terminal.Gui {
 	///   Monitoring of file descriptors is only available on Unix, there
 	///   does not seem to be a way of supporting this on Windows.
 	/// </remarks>
-	public class MainLoop : IDisposable {
+	internal class MainLoop : IDisposable {
 
 		internal SortedList<long, Timeout> _timeouts = new SortedList<long, Timeout> ();
 		readonly object _timeoutsLockToken = new object ();
@@ -67,12 +68,12 @@ namespace Terminal.Gui {
 		/// A shorter limit time can be added at the end, but it will be called before an
 		///  earlier addition that has a longer limit time.
 		/// </summary>
-		public SortedList<long, Timeout> Timeouts => _timeouts;
+		internal SortedList<long, Timeout> Timeouts => _timeouts;
 
 		/// <summary>
 		/// Gets a copy of the list of all idle handlers.
 		/// </summary>
-		public ReadOnlyCollection<Func<bool>> IdleHandlers {
+		internal ReadOnlyCollection<Func<bool>> IdleHandlers {
 			get {
 				lock (_idleHandlersLock) {
 					return new List<Func<bool>> (_idleHandlers).AsReadOnly ();
@@ -84,13 +85,13 @@ namespace Terminal.Gui {
 		/// The current <see cref="IMainLoopDriver"/> in use.
 		/// </summary>
 		/// <value>The main loop driver.</value>
-		public IMainLoopDriver MainLoopDriver { get; private set; }
+		internal IMainLoopDriver MainLoopDriver { get; private set; }
 
 		/// <summary>
 		/// Invoked when a new timeout is added. To be used in the case
-		/// when <see cref="Application.ExitRunLoopAfterFirstIteration"/> is <see langword="true"/>.
+		/// when <see cref="Application.EndAfterFirstIteration"/> is <see langword="true"/>.
 		/// </summary>
-		public event EventHandler<TimeoutEventArgs> TimeoutAdded;
+		internal event EventHandler<TimeoutEventArgs> TimeoutAdded;
 
 		/// <summary>
 		///  Creates a new MainLoop. 
@@ -100,24 +101,12 @@ namespace Terminal.Gui {
 		/// </remarks>
 		/// <param name="driver">The <see cref="ConsoleDriver"/> instance
 		/// (one of the implementations FakeMainLoop, UnixMainLoop, NetMainLoop or WindowsMainLoop).</param>
-		public MainLoop (IMainLoopDriver driver)
+		internal MainLoop (IMainLoopDriver driver)
 		{
 			MainLoopDriver = driver;
 			driver.Setup (this);
 		}
 
-		/// <summary>
-		///   Runs <paramref name="action"/> on the thread that is processing events
-		/// </summary>
-		/// <param name="action">the action to be invoked on the main processing thread.</param>
-		public void Invoke (Action action)
-		{
-			AddIdle (() => {
-				action ();
-				return false;
-			});
-		}
-
 		/// <summary>
 		///   Adds specified idle handler function to <see cref="MainLoop"/> processing. 
 		///   The handler function will be called once per iteration of the main loop after other events have been handled.
@@ -131,7 +120,7 @@ namespace Terminal.Gui {
 		/// </para>
 		/// </remarks>
 		/// <param name="idleHandler">Token that can be used to remove the idle handler with <see cref="RemoveIdle(Func{bool})"/> .</param>
-		public Func<bool> AddIdle (Func<bool> idleHandler)
+		internal Func<bool> AddIdle (Func<bool> idleHandler)
 		{
 			lock (_idleHandlersLock) {
 				_idleHandlers.Add (idleHandler);
@@ -147,7 +136,7 @@ namespace Terminal.Gui {
 		/// <param name="token">A token returned by <see cref="AddIdle(Func{bool})"/></param>
 		/// Returns <c>true</c>if the idle handler is successfully removed; otherwise, <c>false</c>.
 		///  This method also returns <c>false</c> if the idle handler is not found.
-		public bool RemoveIdle (Func<bool> token)
+		internal bool RemoveIdle (Func<bool> token)
 		{
 			lock (_idleHandlersLock) {
 				return _idleHandlers.Remove (token);
@@ -174,7 +163,7 @@ namespace Terminal.Gui {
 		///   The returned value is a token that can be used to stop the timeout
 		///   by calling <see cref="RemoveTimeout(object)"/>.
 		/// </remarks>
-		public object AddTimeout (TimeSpan time, Func<MainLoop, bool> callback)
+		internal object AddTimeout (TimeSpan time, Func<bool> callback)
 		{
 			if (callback == null) {
 				throw new ArgumentNullException (nameof (callback));
@@ -195,7 +184,7 @@ namespace Terminal.Gui {
 		/// </remarks>
 		/// Returns <c>true</c>if the timeout is successfully removed; otherwise, <c>false</c>.
 		/// This method also returns <c>false</c> if the timeout is not found.
-		public bool RemoveTimeout (object token)
+		internal bool RemoveTimeout (object token)
 		{
 			lock (_timeoutsLockToken) {
 				var idx = _timeouts.IndexOfValue (token as Timeout);
@@ -223,7 +212,7 @@ namespace Terminal.Gui {
 
 			foreach ((var k, var timeout) in copy) {
 				if (k < now) {
-					if (timeout.Callback (this)) {
+					if (timeout.Callback ()) {
 						AddTimeout (timeout.Span, timeout);
 					}
 				} else {
@@ -240,7 +229,7 @@ namespace Terminal.Gui {
 		/// <param name="waitTimeout">Returns the number of milliseconds remaining in the current timer (if any). Will be -1 if
 		/// there are no active timers.</param>
 		/// <returns><see langword="true"/> if there is a timer or idle handler active.</returns>
-		public bool CheckTimersAndIdleHandlers (out int waitTimeout)
+		internal bool CheckTimersAndIdleHandlers (out int waitTimeout)
 		{
 			var now = DateTime.UtcNow.Ticks;
 
@@ -275,7 +264,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="k"></param>
 		/// <returns></returns>
-		private long NudgeToUniqueKey (long k)
+		long NudgeToUniqueKey (long k)
 		{
 			lock (_timeoutsLockToken) {
 				while (_timeouts.ContainsKey (k)) {
@@ -303,7 +292,10 @@ namespace Terminal.Gui {
 			}
 		}
 
-		bool _running;
+		/// <summary>
+		/// Used for unit tests.
+		/// </summary>
+		internal bool Running { get; private set; }
 
 		/// <summary>
 		///   Determines whether there are pending events to be processed.
@@ -313,7 +305,7 @@ namespace Terminal.Gui {
 		///   Typically used if you need to flush the input queue while still
 		///   running some of your own code in your main thread.
 		/// </remarks>
-		public bool EventsPending ()
+		internal bool EventsPending ()
 		{
 			return MainLoopDriver.EventsPending ();
 		}
@@ -328,14 +320,14 @@ namespace Terminal.Gui {
 		///     while (main.EventsPending ()) RunIteration ();
 		///   </code>
 		/// </remarks>
-		public void RunIteration ()
+		internal void RunIteration ()
 		{
 			lock (_timeouts) {
 				if (_timeouts.Count > 0) {
 					RunTimers ();
 				}
 			}
-			
+
 			MainLoopDriver.Iteration ();
 
 			var runIdle = false;
@@ -349,34 +341,39 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		///   Runs the <see cref="MainLoop"/>.
+		///   Runs the <see cref="MainLoop"/>. Used only for unit tests.
 		/// </summary>
-		public void Run ()
+		internal void Run ()
 		{
-			var prev = _running;
-			_running = true;
-			while (_running) {
+			var prev = Running;
+			Running = true;
+			while (Running) {
 				EventsPending ();
 				RunIteration ();
 			}
-			_running = prev;
+			Running = prev;
 		}
 
 		/// <summary>
-		/// Stops the main loop driver and calls <see cref="IMainLoopDriver.Wakeup"/>.
+		/// Wakes up the <see cref="MainLoop"/> that might be waiting on input.
 		/// </summary>
-		public void Stop ()
+		internal void Wakeup () => MainLoopDriver?.Wakeup ();
+
+		/// <summary>
+		/// Stops the main loop driver and calls <see cref="IMainLoopDriver.Wakeup"/>. Used only for unit tests.
+		/// </summary>
+		internal void Stop ()
 		{
-			_running = false;
-			MainLoopDriver.Wakeup ();
+			Running = false;
+			Wakeup ();
 		}
-		
+
 		/// <inheritdoc/>
 		public void Dispose ()
 		{
 			GC.SuppressFinalize (this);
 			Stop ();
-			_running = false;
+			Running = false;
 			MainLoopDriver?.TearDown ();
 			MainLoopDriver = null;
 		}

+ 1 - 1
Terminal.Gui/Timeout.cs

@@ -19,5 +19,5 @@ public sealed class Timeout {
 	/// <summary>
 	/// The function that will be invoked.
 	/// </summary>
-	public Func<MainLoop, bool> Callback;
+	public Func<bool> Callback;
 }

+ 1 - 1
Terminal.Gui/TimeoutEventArgs.cs

@@ -5,7 +5,7 @@ namespace Terminal.Gui {
 	/// <summary>
 	/// <see cref="EventArgs"/> for timeout events (e.g. <see cref="MainLoop.TimeoutAdded"/>)
 	/// </summary>
-	public class TimeoutEventArgs : EventArgs {
+	internal class TimeoutEventArgs : EventArgs {
 		/// <summary>
 		/// Gets the timeout callback handler
 		/// </summary>

+ 0 - 32
Terminal.Gui/View/Layout/ResizedEventArgs.cs

@@ -1,32 +0,0 @@
-//
-// Core.cs: The core engine for gui.cs
-//
-// Authors:
-//   Miguel de Icaza ([email protected])
-//
-// Pending:
-//   - Check for NeedDisplay on the hierarchy and repaint
-//   - Layout support
-//   - "Colors" type or "Attributes" type?
-//   - What to surface as "BackgroundCOlor" when clearing a window, an attribute or colors?
-//
-// Optimizations
-//   - Add rendering limitation to the exposed area
-using System;
-
-namespace Terminal.Gui {
-
-	/// <summary>
-	/// Event arguments for the <see cref="Application.TerminalResized"/> event.
-	/// </summary>
-	public class ResizedEventArgs : EventArgs {
-		/// <summary>
-		/// The number of rows in the resized terminal.
-		/// </summary>
-		public int Rows { get; set; }
-		/// <summary>
-		/// The number of columns in the resized terminal.
-		/// </summary>
-		public int Cols { get; set; }
-	}
-}

+ 5 - 0
Terminal.Gui/View/Layout/SizeChangedEventArgs.cs

@@ -22,5 +22,10 @@ namespace Terminal.Gui {
 		/// resolved.
 		/// </summary>
 		public Size Size { get; }
+
+		/// <summary>
+		/// Set to <see langword="true"/> to cause the resize to be cancelled, if appropriate.
+		/// </summary>
+		public bool Cancel { get; set; }
 	}
 }

+ 6 - 6
Terminal.Gui/View/ViewKeyboard.cs

@@ -163,7 +163,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Invoked when a character key is pressed and occurs after the key up event.
 		/// </summary>
-		public event EventHandler<KeyEventEventArgs> KeyPress;
+		public event EventHandler<KeyEventEventArgs> KeyPressed;
 
 		/// <inheritdoc/>
 		public override bool ProcessKey (KeyEvent keyEvent)
@@ -173,11 +173,11 @@ namespace Terminal.Gui {
 			}
 
 			var args = new KeyEventEventArgs (keyEvent);
-			KeyPress?.Invoke (this, args);
+			KeyPressed?.Invoke (this, args);
 			if (args.Handled)
 				return true;
 			if (Focused?.Enabled == true) {
-				Focused?.KeyPress?.Invoke (this, args);
+				Focused?.KeyPressed?.Invoke (this, args);
 				if (args.Handled)
 					return true;
 			}
@@ -348,7 +348,7 @@ namespace Terminal.Gui {
 
 			var args = new KeyEventEventArgs (keyEvent);
 			if (MostFocused?.Enabled == true) {
-				MostFocused?.KeyPress?.Invoke (this, args);
+				MostFocused?.KeyPressed?.Invoke (this, args);
 				if (args.Handled)
 					return true;
 			}
@@ -371,11 +371,11 @@ namespace Terminal.Gui {
 			}
 
 			var args = new KeyEventEventArgs (keyEvent);
-			KeyPress?.Invoke (this, args);
+			KeyPressed?.Invoke (this, args);
 			if (args.Handled)
 				return true;
 			if (MostFocused?.Enabled == true) {
-				MostFocused?.KeyPress?.Invoke (this, args);
+				MostFocused?.KeyPressed?.Invoke (this, args);
 				if (args.Handled)
 					return true;
 			}

+ 1 - 1
Terminal.Gui/View/ViewLayout.cs

@@ -431,7 +431,7 @@ namespace Terminal.Gui {
 
 		/// <summary>
 		/// Called whenever the view needs to be resized. Sets <see cref="Frame"/> and
-		/// triggers a <see cref="LayoutSubviews()"/> call.		/// 
+		/// triggers a <see cref="LayoutSubviews()"/> call.
 		/// </summary>
 		/// <remarks>
 		/// Can be overridden if the view resize behavior is different than the default.

+ 4 - 4
Terminal.Gui/Views/Button.cs

@@ -10,11 +10,11 @@ using System.Text;
 
 namespace Terminal.Gui {
 	/// <summary>
-	///   Button is a <see cref="View"/> that provides an item that invokes an <see cref="Action"/> when activated by the user.
+	///   Button is a <see cref="View"/> that provides an item that invokes raises the <see cref="Clicked"/> event.
 	/// </summary>
 	/// <remarks>
 	/// <para>
-	///   Provides a button showing text invokes an <see cref="Action"/> when clicked on with a mouse
+	///   Provides a button showing text that raises the <see cref="Clicked"/> event when clicked on with a mouse
 	///   or when the user presses SPACE, ENTER, or hotkey. The hotkey is the first letter or digit following the first underscore ('_') 
 	///   in the button text. 
 	/// </para>
@@ -27,7 +27,7 @@ namespace Terminal.Gui {
 	/// <para>
 	///   When the button is configured as the default (<see cref="IsDefault"/>) and the user presses
 	///   the ENTER key, if no other <see cref="View"/> processes the <see cref="KeyEvent"/>, the <see cref="Button"/>'s
-	///   <see cref="Action"/> will be invoked.
+	///   <see cref="Clicked"/> event will will be fired.
 	/// </para>
 	/// </remarks>
 	public class Button : View {
@@ -258,7 +258,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		///   Clicked <see cref="Action"/>, raised when the user clicks the primary mouse button within the Bounds of this <see cref="View"/>
+		///   The event fired when the user clicks the primary mouse button within the Bounds of this <see cref="View"/>
 		///   or if the user presses the action key while this view is focused. (TODO: IsDefault)
 		/// </summary>
 		/// <remarks>

+ 7 - 7
Terminal.Gui/Views/FileDialog.cs

@@ -145,7 +145,7 @@ namespace Terminal.Gui {
 				X = Pos.Function (CalculateOkButtonPosX)
 			};
 			this.btnOk.Clicked += (s, e) => this.Accept (true);
-			this.btnOk.KeyPress += (s, k) => {
+			this.btnOk.KeyPressed += (s, k) => {
 				this.NavigateIf (k, Key.CursorLeft, this.btnCancel);
 				this.NavigateIf (k, Key.CursorUp, this.tableView);
 			};
@@ -154,7 +154,7 @@ namespace Terminal.Gui {
 				Y = Pos.AnchorEnd (1),
 				X = Pos.Right (btnOk) + 1
 			};
-			this.btnCancel.KeyPress += (s, k) => {
+			this.btnCancel.KeyPressed += (s, k) => {
 				this.NavigateIf (k, Key.CursorLeft, this.btnToggleSplitterCollapse);
 				this.NavigateIf (k, Key.CursorUp, this.tableView);
 				this.NavigateIf (k, Key.CursorRight, this.btnOk);
@@ -179,7 +179,7 @@ namespace Terminal.Gui {
 				Width = Dim.Fill (0),
 				CaptionColor = new Color (Color.Black)
 			};
-			this.tbPath.KeyPress += (s, k) => {
+			this.tbPath.KeyPressed += (s, k) => {
 
 				ClearFeedback ();
 
@@ -228,7 +228,7 @@ namespace Terminal.Gui {
 			typeStyle.MinWidth = 6;
 			typeStyle.ColorGetter = this.ColorGetter;
 
-			this.tableView.KeyPress += (s, k) => {
+			this.tableView.KeyPressed += (s, k) => {
 				if (this.tableView.SelectedRow <= 0) {
 					this.NavigateIf (k, Key.CursorUp, this.tbPath);
 				}
@@ -285,7 +285,7 @@ namespace Terminal.Gui {
 			};
 
 			tbFind.TextChanged += (s, o) => RestartSearch ();
-			tbFind.KeyPress += (s, o) => {
+			tbFind.KeyPressed += (s, o) => {
 				if (o.KeyEvent.Key == Key.Enter) {
 					RestartSearch ();
 					o.Handled = true;
@@ -1438,7 +1438,7 @@ namespace Terminal.Gui {
 						UpdateChildrenToFound ();
 					}
 
-					Application.MainLoop.Invoke (() => {
+					Application.Invoke (() => {
 						Parent.spinnerView.Visible = false;
 					});
 				}
@@ -1450,7 +1450,7 @@ namespace Terminal.Gui {
 					Children = found.ToArray ();
 				}
 
-				Application.MainLoop.Invoke (() => {
+				Application.Invoke (() => {
 					Parent.tbPath.Autocomplete.GenerateSuggestions (
 						new AutocompleteFilepathContext (Parent.tbPath.Text, Parent.tbPath.CursorPosition, this)
 						);

+ 1 - 1
Terminal.Gui/Views/Label.cs

@@ -65,7 +65,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		///   Clicked <see cref="Action"/>, raised when the user clicks the primary mouse button within the Bounds of this <see cref="View"/>
+		///   The event fired when the user clicks the primary mouse button within the Bounds of this <see cref="View"/>
 		///   or if the user presses the action key while this view is focused. (TODO: IsDefault)
 		/// </summary>
 		/// <remarks>

+ 12 - 12
Terminal.Gui/Views/Menu.cs

@@ -482,9 +482,9 @@ namespace Terminal.Gui {
 
 			if (Application.Current != null) {
 				Application.Current.DrawContentComplete += Current_DrawContentComplete;
-				Application.Current.TerminalResized += Current_TerminalResized;
+				Application.Current.SizeChanging += Current_TerminalResized;
 			}
-			Application.RootMouseEvent += Application_RootMouseEvent;
+			Application.MouseEvent += Application_RootMouseEvent;
 
 			// Things this view knows how to do
 			AddCommand (Command.LineUp, () => MoveUp ());
@@ -523,15 +523,15 @@ namespace Terminal.Gui {
 		{
 			base.OnVisibleChanged ();
 			if (Visible) {
-				Application.RootMouseEvent += Application_RootMouseEvent;
+				Application.MouseEvent += Application_RootMouseEvent;
 			} else {
-				Application.RootMouseEvent -= Application_RootMouseEvent;
+				Application.MouseEvent -= Application_RootMouseEvent;
 			}
 		}
 
-		private void Application_RootMouseEvent (MouseEvent me)
+		private void Application_RootMouseEvent (object sender, MouseEventEventArgs a)
 		{
-			if (me.View is MenuBar) {
+			if (a.MouseEvent.View is MenuBar) {
 				return;
 			}
 			var locationOffset = host.GetScreenOffsetFromCurrent ();
@@ -539,7 +539,7 @@ namespace Terminal.Gui {
 				locationOffset.X += SuperView.Border.Thickness.Left;
 				locationOffset.Y += SuperView.Border.Thickness.Top;
 			}
-			var view = View.FindDeepestView (this, me.X + locationOffset.X, me.Y + locationOffset.Y, out int rx, out int ry);
+			var view = View.FindDeepestView (this, a.MouseEvent.X + locationOffset.X, a.MouseEvent.Y + locationOffset.Y, out int rx, out int ry);
 			if (view == this) {
 				if (!Visible) {
 					throw new InvalidOperationException ("This shouldn't running on a invisible menu!");
@@ -548,11 +548,11 @@ namespace Terminal.Gui {
 				var nme = new MouseEvent () {
 					X = rx,
 					Y = ry,
-					Flags = me.Flags,
+					Flags = a.MouseEvent.Flags,
 					View = view
 				};
-				if (MouseEvent (nme) || me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1Released) {
-					me.Handled = true;
+				if (MouseEvent (nme) || a.MouseEvent.Flags == MouseFlags.Button1Pressed || a.MouseEvent.Flags == MouseFlags.Button1Released) {
+					a.MouseEvent.Handled = true;
 				}
 			}
 		}
@@ -985,9 +985,9 @@ namespace Terminal.Gui {
 		{
 			if (Application.Current != null) {
 				Application.Current.DrawContentComplete -= Current_DrawContentComplete;
-				Application.Current.TerminalResized -= Current_TerminalResized;
+				Application.Current.SizeChanging -= Current_TerminalResized;
 			}
-			Application.RootMouseEvent -= Application_RootMouseEvent;
+			Application.MouseEvent -= Application_RootMouseEvent;
 			base.Dispose (disposing);
 		}
 	}

+ 1 - 0
Terminal.Gui/Views/Slider.cs

@@ -337,6 +337,7 @@ public class Slider<T> : View {
 	/// </summary>
 	/// <param name="args"></param>
 	/// <returns><see langword="true"/> if the focus change was cancelled.</returns>
+	/// <param name="newFocusedOption"></param>
 	public virtual bool OnOptionFocused (int newFocusedOption, SliderEventArgs<T> args)
 	{
 		if (newFocusedOption > _options.Count - 1 || newFocusedOption < 0) {

+ 5 - 5
Terminal.Gui/Views/SpinnerView/SpinnerView.cs

@@ -153,7 +153,7 @@ namespace Terminal.Gui {
 		/// ignored based on <see cref="SpinDelay"/>.
 		/// </summary>
 		/// <remarks>Ensure this method is called on the main UI
-		/// thread e.g. via <see cref="MainLoop.Invoke(Action)"/>
+		/// thread e.g. via <see cref="Application.Invoke"/>
 		/// </remarks>
 		public void AdvanceAnimation()
 		{
@@ -221,9 +221,9 @@ namespace Terminal.Gui {
 				return;
 			}
 
-			_timeout = Application.MainLoop.AddTimeout (
-				TimeSpan.FromMilliseconds (SpinDelay), (m) => {
-					Application.MainLoop.Invoke (this.AdvanceAnimation);
+			_timeout = Application.AddTimeout (
+				TimeSpan.FromMilliseconds (SpinDelay), () => {
+					Application.Invoke (this.AdvanceAnimation);
 					return true;
 				});
 		}
@@ -232,7 +232,7 @@ namespace Terminal.Gui {
 		private void RemoveAutoSpinTimeout ()
 		{
 			if (_timeout != null) {
-				Application.MainLoop.RemoveTimeout (_timeout);
+				Application.RemoveTimeout (_timeout);
 				_timeout = null;
 			}
 		}

+ 2 - 2
Terminal.Gui/Views/TableView/TreeTableSource.cs

@@ -38,7 +38,7 @@ public class TreeTableSource<T> : IEnumerableTableSource<T>, IDisposable where T
 	{
 		_tableView = table;
 		_tree = tree;
-		_tableView.KeyPress += Table_KeyPress;
+		_tableView.KeyPressed += Table_KeyPress;
 		_tableView.MouseClick += Table_MouseClick;
 
 		var colList = subsequentColumns.Keys.ToList ();
@@ -68,7 +68,7 @@ public class TreeTableSource<T> : IEnumerableTableSource<T>, IDisposable where T
 	/// <inheritdoc/>
 	public void Dispose ()
 	{
-		_tableView.KeyPress -= Table_KeyPress;
+		_tableView.KeyPressed -= Table_KeyPress;
 		_tableView.MouseClick -= Table_MouseClick;
 		_tree.Dispose ();
 	}

+ 8 - 22
Terminal.Gui/Views/Toplevel.cs

@@ -15,7 +15,7 @@ namespace Terminal.Gui {
 	///     been called (which sets the <see cref="Toplevel.Running"/> property to <c>false</c>). 
 	///   </para>
 	///   <para>
-	///     A Toplevel is created when an application initializes Terminal.Gui by calling <see cref="Application.Init(ConsoleDriver, IMainLoopDriver)"/>.
+	///     A Toplevel is created when an application initializes Terminal.Gui by calling <see cref="Application.Init(ConsoleDriver)"/>.
 	///     The application Toplevel can be accessed via <see cref="Application.Top"/>. Additional Toplevels can be created 
 	///     and run (e.g. <see cref="Dialog"/>s. To run a Toplevel, create the <see cref="Toplevel"/> and 
 	///     call <see cref="Application.Run(Toplevel, Func{Exception, bool})"/>.
@@ -98,27 +98,16 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Invoked when the terminal has been resized. The new <see cref="Size"/> of the terminal is provided.
 		/// </summary>
-		public event EventHandler<SizeChangedEventArgs> TerminalResized;
+		public event EventHandler<SizeChangedEventArgs> SizeChanging;
 
-		internal virtual void OnTerminalResized (SizeChangedEventArgs size)
-		{
-			TerminalResized?.Invoke (this, size);
-		}
+		// TODO: Make cancelable?
+		internal virtual void OnSizeChanging (SizeChangedEventArgs size) => SizeChanging?.Invoke (this, size);
 
-		internal virtual void OnChildUnloaded (Toplevel top)
-		{
-			ChildUnloaded?.Invoke (this, new ToplevelEventArgs (top));
-		}
+		internal virtual void OnChildUnloaded (Toplevel top) => ChildUnloaded?.Invoke (this, new ToplevelEventArgs (top));
 
-		internal virtual void OnChildLoaded (Toplevel top)
-		{
-			ChildLoaded?.Invoke (this, new ToplevelEventArgs (top));
-		}
+		internal virtual void OnChildLoaded (Toplevel top) => ChildLoaded?.Invoke (this, new ToplevelEventArgs (top));
 
-		internal virtual void OnClosed (Toplevel top)
-		{
-			Closed?.Invoke (this, new ToplevelEventArgs (top));
-		}
+		internal virtual void OnClosed (Toplevel top) => Closed?.Invoke (this, new ToplevelEventArgs (top));
 
 		internal virtual bool OnClosing (ToplevelClosingEventArgs ev)
 		{
@@ -126,10 +115,7 @@ namespace Terminal.Gui {
 			return ev.Cancel;
 		}
 
-		internal virtual void OnAllChildClosed ()
-		{
-			AllChildClosed?.Invoke (this, EventArgs.Empty);
-		}
+		internal virtual void OnAllChildClosed () => AllChildClosed?.Invoke (this, EventArgs.Empty);
 
 		internal virtual void OnChildClosed (Toplevel top)
 		{

+ 20 - 20
Terminal.Gui/Views/ToplevelOverlapped.cs

@@ -30,7 +30,7 @@ namespace Terminal.Gui {
 			get {
 				if (OverlappedTop != null) {
 					List<Toplevel> _overlappedChildren = new List<Toplevel> ();
-					foreach (var top in _toplevels) {
+					foreach (var top in _topLevels) {
 						if (top != OverlappedTop && !top.Modal) {
 							_overlappedChildren.Add (top);
 						}
@@ -71,9 +71,9 @@ namespace Terminal.Gui {
 				return null;
 			}
 
-			int count = _toplevels.Count;
+			int count = _topLevels.Count;
 			for (int i = count - 1; i >= 0; i--) {
-				foreach (var top in _toplevels) {
+				foreach (var top in _topLevels) {
 					var rx = x - startFrame.X;
 					var ry = y - startFrame.Y;
 					if (top.Visible && top.Frame.Contains (rx, ry)) {
@@ -96,7 +96,7 @@ namespace Terminal.Gui {
 				return false;
 			}
 
-			foreach (var top in _toplevels) {
+			foreach (var top in _topLevels) {
 				if (top != Current && top.Visible && (top.NeedsDisplay || top.SubViewNeedsDisplay || top.LayoutNeeded)) {
 					OverlappedTop.SetSubViewNeedsDisplay ();
 					return true;
@@ -124,19 +124,19 @@ namespace Terminal.Gui {
 		public static void OverlappedMoveNext ()
 		{
 			if (OverlappedTop != null && !Current.Modal) {
-				lock (_toplevels) {
-					_toplevels.MoveNext ();
+				lock (_topLevels) {
+					_topLevels.MoveNext ();
 					var isOverlapped = false;
-					while (_toplevels.Peek () == OverlappedTop || !_toplevels.Peek ().Visible) {
-						if (!isOverlapped && _toplevels.Peek () == OverlappedTop) {
+					while (_topLevels.Peek () == OverlappedTop || !_topLevels.Peek ().Visible) {
+						if (!isOverlapped && _topLevels.Peek () == OverlappedTop) {
 							isOverlapped = true;
-						} else if (isOverlapped && _toplevels.Peek () == OverlappedTop) {
+						} else if (isOverlapped && _topLevels.Peek () == OverlappedTop) {
 							MoveCurrent (Top);
 							break;
 						}
-						_toplevels.MoveNext ();
+						_topLevels.MoveNext ();
 					}
-					Current = _toplevels.Peek ();
+					Current = _topLevels.Peek ();
 				}
 			}
 		}
@@ -147,19 +147,19 @@ namespace Terminal.Gui {
 		public static void OverlappedMovePrevious ()
 		{
 			if (OverlappedTop != null && !Current.Modal) {
-				lock (_toplevels) {
-					_toplevels.MovePrevious ();
+				lock (_topLevels) {
+					_topLevels.MovePrevious ();
 					var isOverlapped = false;
-					while (_toplevels.Peek () == OverlappedTop || !_toplevels.Peek ().Visible) {
-						if (!isOverlapped && _toplevels.Peek () == OverlappedTop) {
+					while (_topLevels.Peek () == OverlappedTop || !_topLevels.Peek ().Visible) {
+						if (!isOverlapped && _topLevels.Peek () == OverlappedTop) {
 							isOverlapped = true;
-						} else if (isOverlapped && _toplevels.Peek () == OverlappedTop) {
+						} else if (isOverlapped && _topLevels.Peek () == OverlappedTop) {
 							MoveCurrent (Top);
 							break;
 						}
-						_toplevels.MovePrevious ();
+						_topLevels.MovePrevious ();
 					}
-					Current = _toplevels.Peek ();
+					Current = _topLevels.Peek ();
 				}
 			}
 		}
@@ -172,8 +172,8 @@ namespace Terminal.Gui {
 		public static bool MoveToOverlappedChild (Toplevel top)
 		{
 			if (top.Visible && OverlappedTop != null && Current?.Modal == false) {
-				lock (_toplevels) {
-					_toplevels.MoveTo (top, 0, new ToplevelEqualityComparer ());
+				lock (_topLevels) {
+					_topLevels.MoveTo (top, 0, new ToplevelEqualityComparer ());
 					Current = top;
 				}
 				return true;

+ 1 - 0
Terminal.sln

@@ -24,6 +24,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
 		nuget.config = nuget.config
 		.github\workflows\publish.yml = .github\workflows\publish.yml
 		README.md = README.md
+		Terminal.Gui\.vscode\settings.json = Terminal.Gui\.vscode\settings.json
 		Terminal.sln.DotSettings = Terminal.sln.DotSettings
 		testenvironments.json = testenvironments.json
 	EndProjectSection

+ 2 - 2
UICatalog/KeyBindingsDialog.cs

@@ -35,7 +35,7 @@ namespace UICatalog {
 				RecordView (top);
 
 				// Refresh known windows
-				Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (100), (m) => {
+				Application.AddTimeout (TimeSpan.FromMilliseconds (100), () => {
 
 					lock (lockKnownViews) {
 						RecordView (Application.Top);
@@ -180,7 +180,7 @@ namespace UICatalog {
 
 			// prompt user to hit a key
 			var dlg = new Dialog () { Title = "Enter Key" };
-			dlg.KeyPress += (s, k) => {
+			dlg.KeyPressed += (s, k) => {
 				key = k.KeyEvent.Key;
 				Application.RequestStop ();
 			};

+ 1 - 1
UICatalog/Properties/launchSettings.json

@@ -3,7 +3,7 @@
     "UICatalog": {
       "commandName": "Project",
       "environmentVariables": {
-        "WT_SESSION": "1"
+        "WT_SESSION": "yes"
       }
     },
     "WSL : UICatalog": {

+ 2 - 2
UICatalog/Scenarios/ASCIICustomButton.cs

@@ -205,7 +205,7 @@ namespace UICatalog.Scenarios {
 					button.Clicked += Button_Clicked;
 					button.PointerEnter += Button_PointerEnter;
 					button.MouseClick += Button_MouseClick;
-					button.KeyPress += Button_KeyPress;
+					button.KeyPressed += Button_KeyPress;
 					scrollView.Add (button);
 					buttons.Add (button);
 					prevButton = button;
@@ -215,7 +215,7 @@ namespace UICatalog.Scenarios {
 				closeButton.Clicked += Button_Clicked;
 				closeButton.PointerEnter += Button_PointerEnter;
 				closeButton.MouseClick += Button_MouseClick;
-				closeButton.KeyPress += Button_KeyPress;
+				closeButton.KeyPressed += Button_KeyPress;
 				scrollView.Add (closeButton);
 				buttons.Add (closeButton);
 

+ 1 - 1
UICatalog/Scenarios/Animation.cs

@@ -53,7 +53,7 @@ namespace UICatalog.Scenarios {
 			Task.Run (() => {
 				while (!isDisposed) {
 					// When updating from a Thread/Task always use Invoke
-					Application.MainLoop.Invoke (() => {
+					Application.Invoke (() => {
 						imageView.NextFrame ();
 						imageView.SetNeedsDisplay ();
 					});

+ 2 - 2
UICatalog/Scenarios/BackgroundWorkerCollection.cs

@@ -56,7 +56,7 @@ namespace UICatalog.Scenarios {
 
 				Closed += OverlappedMain_Closed;
 
-				Application.Iteration += () => {
+				Application.Iteration += (s, a) => {
 					if (canOpenWorkerApp && !workerApp.Running && Application.OverlappedTop.Running) {
 						Application.Run (workerApp);
 					}
@@ -338,7 +338,7 @@ namespace UICatalog.Scenarios {
 				close.Clicked += OnReportClosed;
 				Add (close);
 
-				KeyPress += (s, e) => {
+				KeyPressed += (s, e) => {
 					if (e.KeyEvent.Key == Key.Esc) {
 						OnReportClosed (this, EventArgs.Empty);
 					}

+ 4 - 4
UICatalog/Scenarios/BasicColors.cs

@@ -86,10 +86,10 @@ namespace UICatalog.Scenarios {
 			};
 			Win.Add (viewBackground);
 
-			Application.RootMouseEvent = (e) => {
-				if (e.View != null) {
-					var fore = e.View.GetNormalColor ().Foreground;
-					var back = e.View.GetNormalColor ().Background;
+			Application.MouseEvent += (s, e) => {
+				if (e.MouseEvent.View != null) {
+					var fore = e.MouseEvent.View.GetNormalColor ().Foreground;
+					var back = e.MouseEvent.View.GetNormalColor ().Background;
 					lblForeground.Text = $"#{fore.R:X2}{fore.G:X2}{fore.B:X2} {fore.ColorName} ";
 					viewForeground.ColorScheme.Normal = new Attribute (fore, fore);
 					lblBackground.Text = $"#{back.R:X2}{back.G:X2}{back.B:X2} {back.ColorName} ";

+ 1 - 1
UICatalog/Scenarios/CharacterMap.cs

@@ -604,7 +604,7 @@ class CharMap : ScrollView {
 				decResponse = await client.GetCodepointDec ((int)SelectedCodePoint);
 			} catch (HttpRequestException e) {
 				(s as Dialog).Text = e.Message;
-				Application.MainLoop.Invoke (() => {
+				Application.Invoke (() => {
 					spinner.Visible = false;
 					errorLabel.Text = e.Message;
 					errorLabel.ColorScheme = Colors.ColorSchemes ["Error"];

+ 5 - 5
UICatalog/Scenarios/ContextMenus.cs

@@ -58,7 +58,7 @@ namespace UICatalog.Scenarios {
 
 			Point mousePos = default;
 
-			Win.KeyPress += (s, e) => {
+			Win.KeyPressed += (s, e) => {
 				if (e.KeyEvent.Key == (Key.Space | Key.CtrlMask)) {
 					ShowContextMenu (mousePos.X, mousePos.Y);
 					e.Handled = true;
@@ -72,18 +72,18 @@ namespace UICatalog.Scenarios {
 				}
 			};
 
-			Application.RootMouseEvent += Application_RootMouseEvent;
+			Application.MouseEvent += ApplicationMouseEvent;
 
-			void Application_RootMouseEvent (MouseEvent me)
+			void ApplicationMouseEvent (object sender, MouseEventEventArgs a)
 			{
-				mousePos = new Point (me.X, me.Y);
+				mousePos = new Point (a.MouseEvent.X, a.MouseEvent.Y);
 			}
 
 			Win.WantMousePositionReports = true;
 
 			Application.Top.Closed += (s,e) => {
 				Thread.CurrentThread.CurrentUICulture = new CultureInfo ("en-US");
-				Application.RootMouseEvent -= Application_RootMouseEvent;
+				Application.MouseEvent -= ApplicationMouseEvent;
 			};
 		}
 

+ 1 - 1
UICatalog/Scenarios/CsvEditor.cs

@@ -88,7 +88,7 @@ namespace UICatalog.Scenarios {
 
 			tableView.SelectedCellChanged += OnSelectedCellChanged;
 			tableView.CellActivated += EditCurrentCell;
-			tableView.KeyPress += TableViewKeyPress;
+			tableView.KeyPressed += TableViewKeyPress;
 
 			SetupScrollBar ();
 		}

+ 1 - 1
UICatalog/Scenarios/Editor.cs

@@ -176,7 +176,7 @@ namespace UICatalog.Scenarios {
 				_scrollBar.Refresh ();
 			};
 
-			Win.KeyPress += (s, e) => {
+			Win.KeyPressed += (s, e) => {
 				var keys = ShortcutHelper.GetModifiersKey (e.KeyEvent);
 				if (_winDialog != null && (e.KeyEvent.Key == Key.Esc
 					|| e.KeyEvent.Key == Application.QuitKey)) {

+ 2 - 2
UICatalog/Scenarios/GraphViewExample.cs

@@ -566,7 +566,7 @@ namespace UICatalog.Scenarios {
 			var series = new DiscoBarSeries ();
 			var bars = new List<BarSeriesBar> ();
 
-			Func<MainLoop, bool> genSample = (l) => {
+			Func<bool> genSample = () => {
 
 				bars.Clear ();
 				// generate an imaginary sample
@@ -582,7 +582,7 @@ namespace UICatalog.Scenarios {
 				return graphView.Series.Contains (series);
 			};
 
-			Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (250), genSample);
+			Application.AddTimeout (TimeSpan.FromMilliseconds (250), genSample);
 
 			series.Bars = bars;
 

+ 1 - 1
UICatalog/Scenarios/InteractiveTree.cs

@@ -30,7 +30,7 @@ namespace UICatalog.Scenarios {
 				Width = Dim.Fill (),
 				Height = Dim.Fill (1),
 			};
-			treeView.KeyPress += TreeView_KeyPress;
+			treeView.KeyPressed += TreeView_KeyPress;
 
 			Win.Add (treeView);
 

+ 19 - 18
UICatalog/Scenarios/Keys.cs

@@ -3,7 +3,7 @@ using System.Collections.Generic;
 using Terminal.Gui;
 
 namespace UICatalog.Scenarios {
-	[ScenarioMetadata (Name: "Keys", Description: "Shows how to handle keyboard input")]
+	[ScenarioMetadata (Name: "Keys", Description: "Shows keyboard input handling.")]
 	[ScenarioCategory ("Mouse and Keyboard")]
 	public class Keys : Scenario {
 
@@ -37,7 +37,7 @@ namespace UICatalog.Scenarios {
 			Application.Init ();
 			ConfigurationManager.Themes.Theme = Theme;
 			ConfigurationManager.Apply ();
-			
+
 			Win = new TestWindow () {
 				Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
 				X = 0,
@@ -65,7 +65,7 @@ namespace UICatalog.Scenarios {
 			Win.Add (edit);
 
 			// Last KeyPress: ______
-			var keyPressedLabel = new Label ("Last KeyPress:") {
+			var keyPressedLabel = new Label ("Last Application.KeyPress:") {
 				X = Pos.Left (editLabel),
 				Y = Pos.Top (editLabel) + 2,
 			};
@@ -79,10 +79,10 @@ namespace UICatalog.Scenarios {
 			};
 			Win.Add (labelKeypress);
 
-			Win.KeyPress += (s,e) => labelKeypress.Text = e.KeyEvent.ToString ();
+			Win.KeyPressed += (s, e) => labelKeypress.Text = e.KeyEvent.ToString ();
 
 			// Key stroke log:
-			var keyLogLabel = new Label ("Key stroke log:") {
+			var keyLogLabel = new Label ("Key event log:") {
 				X = Pos.Left (editLabel),
 				Y = Pos.Top (editLabel) + 4,
 			};
@@ -94,19 +94,19 @@ namespace UICatalog.Scenarios {
 			});
 			var maxLogEntry = $"Key{"",-5}: {fakeKeyPress}".Length;
 			var yOffset = (Application.Top == Application.Top ? 1 : 6);
-			var keyStrokelist = new List<string> ();
-			var keyStrokeListView = new ListView (keyStrokelist) {
+			var keyEventlist = new List<string> ();
+			var keyEventListView = new ListView (keyEventlist) {
 				X = 0,
 				Y = Pos.Top (keyLogLabel) + yOffset,
 				Width = Dim.Percent (30),
 				Height = Dim.Fill (),
 			};
-			keyStrokeListView.ColorScheme = Colors.TopLevel;
-			Win.Add (keyStrokeListView);
+			keyEventListView.ColorScheme = Colors.TopLevel;
+			Win.Add (keyEventListView);
 
 			// ProcessKey log:
 			var processKeyLogLabel = new Label ("ProcessKey log:") {
-				X = Pos.Right (keyStrokeListView) + 1,
+				X = Pos.Right (keyEventListView) + 1,
 				Y = Pos.Top (editLabel) + 4,
 			};
 			Win.Add (processKeyLogLabel);
@@ -116,7 +116,7 @@ namespace UICatalog.Scenarios {
 			var processKeyListView = new ListView (((TestWindow)Win)._processKeyList) {
 				X = Pos.Left (processKeyLogLabel),
 				Y = Pos.Top (processKeyLogLabel) + yOffset,
-				Width = Dim.Percent(30),
+				Width = Dim.Percent (30),
 				Height = Dim.Fill (),
 			};
 			processKeyListView.ColorScheme = Colors.TopLevel;
@@ -156,15 +156,16 @@ namespace UICatalog.Scenarios {
 				Height = Dim.Fill (),
 			};
 
-			Win.KeyDown += (s,a) => KeyDownPressUp (a.KeyEvent, "Down");
-			Win.KeyPress += (s, a) => KeyDownPressUp (a.KeyEvent, "Press");
-			Win.KeyUp += (s, a) => KeyDownPressUp (a.KeyEvent, "Up");
+			Application.KeyDown += (s, a) => KeyDownPressUp (a, "Down");
+			Application.KeyPressed += (s, a) => KeyDownPressUp (a, "Press");
+			Application.KeyUp += (s, a) => KeyDownPressUp (a, "Up");
 
-			void KeyDownPressUp (KeyEvent keyEvent, string updown)
+			void KeyDownPressUp (KeyEventEventArgs args, string updown)
 			{
-				var msg = $"Key{updown,-5}: {keyEvent}";
-				keyStrokelist.Add (msg);
-				keyStrokeListView.MoveDown ();
+				// BUGBUG: KeyEvent.ToString is badly broken
+				var msg = $"Key{updown,-5}: {args.KeyEvent}";
+				keyEventlist.Add (msg);
+				keyEventListView.MoveDown ();
 				processKeyListView.MoveDown ();
 				processColdKeyListView.MoveDown ();
 				processHotKeyListView.MoveDown ();

+ 1 - 1
UICatalog/Scenarios/LineDrawing.cs

@@ -32,7 +32,7 @@ namespace UICatalog.Scenarios {
 			Win.Add (canvas);
 			Win.Add (tools);
 
-			Win.KeyPress += (s,e) => { e.Handled = canvas.ProcessKey (e.KeyEvent); };
+			Win.KeyPressed += (s,e) => { e.Handled = canvas.ProcessKey (e.KeyEvent); };
 		}
 
 		class ToolsView : Window {

+ 1 - 1
UICatalog/Scenarios/ListColumns.cs

@@ -101,7 +101,7 @@ namespace UICatalog.Scenarios {
 			Win.Add (selectedCellLabel);
 
 			listColView.SelectedCellChanged += (s, e) => { selectedCellLabel.Text = $"{listColView.SelectedRow},{listColView.SelectedColumn}"; };
-			listColView.KeyPress += TableViewKeyPress;
+			listColView.KeyPressed += TableViewKeyPress;
 
 			SetupScrollBar ();
 

+ 3 - 3
UICatalog/Scenarios/Mouse.cs

@@ -27,9 +27,9 @@ namespace UICatalog.Scenarios {
 			};
 			Win.Add (rmeList);
 
-			Application.RootMouseEvent += delegate (MouseEvent me) {
-				ml.Text = $"Mouse: ({me.X},{me.Y}) - {me.Flags} {count}";
-				rme.Add ($"({me.X},{me.Y}) - {me.Flags} {count++}");
+			Application.MouseEvent += (sender, a) => {
+				ml.Text = $"Mouse: ({a.MouseEvent.X},{a.MouseEvent.Y}) - {a.MouseEvent.Flags} {count}";
+				rme.Add ($"({a.MouseEvent.X},{a.MouseEvent.Y}) - {a.MouseEvent.Flags} {count++}");
 				rmeList.MoveDown ();
 			};
 

+ 2 - 2
UICatalog/Scenarios/ProcessTable.cs

@@ -31,8 +31,8 @@ namespace UICatalog.Scenarios {
 			CreateProcessTable ();
 
 			// Then every second
-			Application.MainLoop.AddTimeout (TimeSpan.FromSeconds (1),
-				(s) => {
+			Application.AddTimeout (TimeSpan.FromSeconds (1),
+				() => {
 					CreateProcessTable ();
 					return true;
 				});

+ 6 - 6
UICatalog/Scenarios/Progress.cs

@@ -123,7 +123,7 @@ namespace UICatalog.Scenarios {
 			{
 				Started = true;
 				StartBtnClick?.Invoke ();
-				Application.MainLoop.Invoke(()=>{
+				Application.Invoke(()=>{
 					Spinner.Visible = true;
 					ActivityProgressBar.Width = Dim.Fill () - Spinner.Width;
 					this.LayoutSubviews();
@@ -135,7 +135,7 @@ namespace UICatalog.Scenarios {
 				Started = false;
 				StopBtnClick?.Invoke ();
 
-				Application.MainLoop.Invoke(()=>{
+				Application.Invoke(()=>{
 					Spinner.Visible = false;
 					ActivityProgressBar.Width = Dim.Fill () - Spinner.Width;
 					this.LayoutSubviews();
@@ -182,7 +182,7 @@ namespace UICatalog.Scenarios {
 				_systemTimer = new Timer ((o) => {
 					// Note the check for Mainloop being valid. System.Timers can run after they are Disposed.
 					// This code must be defensive for that. 
-					Application.MainLoop?.Invoke (() => systemTimerDemo.Pulse ());
+					Application.Invoke (() => systemTimerDemo.Pulse ());
 				}, null, 0, _systemTimerTick);
 			};
 
@@ -209,7 +209,7 @@ namespace UICatalog.Scenarios {
 			};
 			Win.Add (systemTimerDemo);
 
-			// Demo #2 - Use Application.MainLoop.AddTimeout (no threads)
+			// Demo #2 - Use Application.AddTimeout (no threads)
 			var mainLoopTimeoutDemo = new ProgressDemo ("Application.AddTimer (no threads)") {
 				X = 0,
 				Y = Pos.Bottom (systemTimerDemo),
@@ -221,7 +221,7 @@ namespace UICatalog.Scenarios {
 				mainLoopTimeoutDemo.ActivityProgressBar.Fraction = 0F;
 				mainLoopTimeoutDemo.PulseProgressBar.Fraction = 0F;
 
-				_mainLoopTimeout = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (_mainLooopTimeoutTick), (loop) => {
+				_mainLoopTimeout = Application.AddTimeout (TimeSpan.FromMilliseconds (_mainLooopTimeoutTick), () => {
 					mainLoopTimeoutDemo.Pulse ();
 					
 					return true;
@@ -229,7 +229,7 @@ namespace UICatalog.Scenarios {
 			};
 			mainLoopTimeoutDemo.StopBtnClick = () => {
 				if (_mainLoopTimeout != null) {
-					Application.MainLoop.RemoveTimeout (_mainLoopTimeout);
+					Application.RemoveTimeout (_mainLoopTimeout);
 					_mainLoopTimeout = null;
 				}
 

+ 2 - 2
UICatalog/Scenarios/ProgressBarStyles.cs

@@ -79,7 +79,7 @@ namespace UICatalog.Scenarios {
 							_fractionTimer = null;
 							button.Enabled = true;
 						}
-						Application.MainLoop.MainLoopDriver.Wakeup ();
+						Application.Wakeup ();
 					}, null, 0, _timerTick);
 				}
 			};
@@ -128,7 +128,7 @@ namespace UICatalog.Scenarios {
 				marqueesBlocksPB.Text = marqueesContinuousPB.Text = DateTime.Now.TimeOfDay.ToString ();
 				marqueesBlocksPB.Pulse ();
 				marqueesContinuousPB.Pulse ();
-				Application.MainLoop.MainLoopDriver.Wakeup ();
+				Application.Wakeup ();
 			}, null, 0, 300);
 
 			Application.Top.Unloaded += Top_Unloaded;

+ 4 - 4
UICatalog/Scenarios/Scrolling.cs

@@ -291,8 +291,8 @@ namespace UICatalog.Scenarios {
 				Width = 50,
 			};
 			Win.Add (mousePos);
-			Application.RootMouseEvent += delegate (MouseEvent me) {
-				mousePos.Text = $"Mouse: ({me.X},{me.Y}) - {me.Flags} {count++}";
+			Application.MouseEvent += (sender, a) => {
+				mousePos.Text = $"Mouse: ({a.MouseEvent.X},{a.MouseEvent.Y}) - {a.MouseEvent.Flags} {count++}";
 			};
 
 			var progress = new ProgressBar {
@@ -303,12 +303,12 @@ namespace UICatalog.Scenarios {
 			Win.Add (progress);
 
 			bool pulsing = true;
-			bool timer (MainLoop caller)
+			bool timer ()
 			{
 				progress.Pulse ();
 				return pulsing;
 			}
-			Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (300), timer);
+			Application.AddTimeout (TimeSpan.FromMilliseconds (300), timer);
 
 			void Top_Unloaded (object sender, EventArgs args)
 			{

+ 2 - 2
UICatalog/Scenarios/SendKeys.cs

@@ -57,7 +57,7 @@ namespace UICatalog.Scenarios {
 			var IsAlt = false;
 			var IsCtrl = false;
 
-			txtResult.KeyPress += (s, e) => {
+			txtResult.KeyPressed += (s, e) => {
 				rKeys += (char)e.KeyEvent.Key;
 				if (!IsShift && e.KeyEvent.IsShift) {
 					rControlKeys += " Shift ";
@@ -116,7 +116,7 @@ namespace UICatalog.Scenarios {
 
 			button.Clicked += (s,e) => ProcessInput ();
 
-			Win.KeyPress += (s, e) => {
+			Win.KeyPressed += (s, e) => {
 				if (e.KeyEvent.Key == Key.Enter) {
 					ProcessInput ();
 					e.Handled = true;

+ 1 - 1
UICatalog/Scenarios/SingleBackgroundWorker.cs

@@ -133,7 +133,7 @@ namespace UICatalog.Scenarios {
 			public StagingUIController (DateTime? start, List<string> list)
 			{
 				top = new Toplevel (Application.Top.Frame);
-				top.KeyPress += (s,e) => {
+				top.KeyPressed += (s,e) => {
 					// Prevents Ctrl+Q from closing this.
 					// Only Ctrl+C is allowed.
 					if (e.KeyEvent.Key == Application.QuitKey) {

+ 1 - 1
UICatalog/Scenarios/Snake.cs

@@ -38,7 +38,7 @@ namespace UICatalog.Scenarios {
 					if (state.AdvanceState ()) {
 
 						// When updating from a Thread/Task always use Invoke
-						Application.MainLoop?.Invoke (() => {
+						Application.Invoke (() => {
 							snakeView.SetNeedsDisplay ();
 						});
 					}

+ 1 - 1
UICatalog/Scenarios/TableEditor.cs

@@ -122,7 +122,7 @@ namespace UICatalog.Scenarios {
 
 			tableView.SelectedCellChanged += (s, e) => { selectedCellLabel.Text = $"{tableView.SelectedRow},{tableView.SelectedColumn}"; };
 			tableView.CellActivated += EditCurrentCell;
-			tableView.KeyPress += TableViewKeyPress;
+			tableView.KeyPressed += TableViewKeyPress;
 
 			SetupScrollBar ();
 

+ 1 - 1
UICatalog/Scenarios/Threading.cs

@@ -45,7 +45,7 @@ namespace UICatalog.Scenarios {
 			};
 
 			_btnActionCancel = new Button (1, 1, "Cancelable Load Items");
-			_btnActionCancel.Clicked += (s, e) => Application.MainLoop.Invoke (CallLoadItemsAsync);
+			_btnActionCancel.Clicked += (s, e) => Application.Invoke (CallLoadItemsAsync);
 
 			Win.Add (new Label ("Data Items:") {
 				X = Pos.X (_btnActionCancel),

+ 2 - 2
UICatalog/Scenarios/TreeViewFileSystem.cs

@@ -95,7 +95,7 @@ namespace UICatalog.Scenarios {
 
 			Win.Add (_detailsFrame);
 			treeViewFiles.MouseClick += TreeViewFiles_MouseClick;
-			treeViewFiles.KeyPress += TreeViewFiles_KeyPress;
+			treeViewFiles.KeyPressed += TreeViewFiles_KeyPress;
 			treeViewFiles.SelectionChanged += TreeViewFiles_SelectionChanged;
 
 			SetupFileTree ();
@@ -206,7 +206,7 @@ namespace UICatalog.Scenarios {
 
 			menu.MenuItems = new MenuBarItem (new [] { new MenuItem ("Properties", null, () => ShowPropertiesOf (forObject)) });
 
-			Application.MainLoop.Invoke (menu.Show);
+			Application.Invoke (menu.Show);
 		}
 
 		class DetailsFrame : FrameView {

+ 3 - 3
UICatalog/Scenarios/TrueColors.cs

@@ -82,9 +82,9 @@ namespace UICatalog.Scenarios {
 			};
 			Win.Add (lblBlue);
 			
-			Application.RootMouseEvent = (e) => {
-				if (e.View != null) {
-					var normal = e.View.GetNormalColor ();
+			Application.MouseEvent += (s, e) => {
+				if (e.MouseEvent.View != null) {
+					var normal = e.MouseEvent.View.GetNormalColor ();
 					lblRed.Text = normal.Foreground.R.ToString ();
 					lblGreen.Text = normal.Foreground.G.ToString ();
 					lblBlue.Text = normal.Foreground.B.ToString ();

+ 5 - 5
UICatalog/Scenarios/VkeyPacketSimulator.cs

@@ -96,13 +96,13 @@ namespace UICatalog.Scenarios {
 				}
 			};
 
-			tvOutput.KeyPress += (s, e) => {
+			tvOutput.KeyPressed += (s, e) => {
 				//System.Diagnostics.Debug.WriteLine ($"Output - KeyPress - _keyboardStrokes: {_keyboardStrokes.Count}");
 				if (_outputStarted && _keyboardStrokes.Count > 0) {
 					var ev = ShortcutHelper.GetModifiersKey (e.KeyEvent);
 					//System.Diagnostics.Debug.WriteLine ($"Output - KeyPress: {ev}");
 					if (!tvOutput.ProcessKey (e.KeyEvent)) {
-						Application.MainLoop.Invoke (() => {
+						Application.Invoke (() => {
 							MessageBox.Query ("Keys", $"'{ShortcutHelper.GetShortcutTag (ev)}' pressed!", "Ok");
 						});
 					}
@@ -124,7 +124,7 @@ namespace UICatalog.Scenarios {
 
 			KeyEventEventArgs unknownChar = null;
 
-			tvInput.KeyPress += (s, e) => {
+			tvInput.KeyPressed += (s, e) => {
 				if (e.KeyEvent.Key == (Key.Q | Key.CtrlMask)) {
 					Application.RequestStop ();
 					return;
@@ -186,7 +186,7 @@ namespace UICatalog.Scenarios {
 								}
 								//}
 							} catch (Exception) {
-								Application.MainLoop.Invoke (() => {
+								Application.Invoke (() => {
 									MessageBox.ErrorQuery ("Error", "Couldn't send the keystrokes!", "Ok");
 									Application.RequestStop ();
 								});
@@ -196,7 +196,7 @@ namespace UICatalog.Scenarios {
 							_keyboardStrokes.RemoveAt (0);
 							if (_keyboardStrokes.Count == 0) {
 								_outputStarted = false;
-								Application.MainLoop.Invoke (() => {
+								Application.Invoke (() => {
 									tvOutput.ReadOnly = true;
 									tvInput.SetFocus ();
 								});

+ 859 - 859
UnitTests/Application/ApplicationTests.cs

@@ -9,1001 +9,1001 @@ using Xunit;
 // Alias Console to MockConsole so we don't accidentally use Console
 using Console = Terminal.Gui.FakeConsole;
 
-namespace Terminal.Gui.ApplicationTests {
-	public class ApplicationTests {
-		public ApplicationTests ()
-		{
+namespace Terminal.Gui.ApplicationTests;
+
+public class ApplicationTests {
+	public ApplicationTests ()
+	{
 #if DEBUG_IDISPOSABLE
-			Responder.Instances.Clear ();
-			RunState.Instances.Clear ();
+		Responder.Instances.Clear ();
+		RunState.Instances.Clear ();
 #endif
-		}
+	}
 
-		void Pre_Init_State ()
-		{
-			Assert.Null (Application.Driver);
-			Assert.Null (Application.Top);
-			Assert.Null (Application.Current);
-			Assert.Null (Application.MainLoop);
-			Assert.Null (Application.Iteration);
-			Assert.Null (Application.RootMouseEvent);
-			Assert.Null (Application.TerminalResized);
-		}
+	void Pre_Init_State ()
+	{
+		Assert.Null (Application.Driver);
+		Assert.Null (Application.Top);
+		Assert.Null (Application.Current);
+		Assert.Null (Application.MainLoop);
+	}
 
-		void Post_Init_State ()
-		{
-			Assert.NotNull (Application.Driver);
-			Assert.NotNull (Application.Top);
-			Assert.NotNull (Application.Current);
-			Assert.NotNull (Application.MainLoop);
-			Assert.Null (Application.Iteration);
-			Assert.Null (Application.RootMouseEvent);
-			Assert.Null (Application.TerminalResized);
-			// FakeDriver is always 80x25
-			Assert.Equal (80, Application.Driver.Cols);
-			Assert.Equal (25, Application.Driver.Rows);
+	void Post_Init_State ()
+	{
+		Assert.NotNull (Application.Driver);
+		Assert.NotNull (Application.Top);
+		Assert.NotNull (Application.Current);
+		Assert.NotNull (Application.MainLoop);
+		// FakeDriver is always 80x25
+		Assert.Equal (80, Application.Driver.Cols);
+		Assert.Equal (25, Application.Driver.Rows);
 
-		}
+	}
 
-		void Init ()
-		{
-			Application.Init (new FakeDriver ());
-			Assert.NotNull (Application.Driver);
-			Assert.NotNull (Application.MainLoop);
-			Assert.NotNull (SynchronizationContext.Current);
-		}
+	void Init ()
+	{
+		Application.Init (new FakeDriver ());
+		Assert.NotNull (Application.Driver);
+		Assert.NotNull (Application.MainLoop);
+		Assert.NotNull (SynchronizationContext.Current);
+	}
 
-		void Shutdown ()
-		{
-			Application.Shutdown ();
-		}
+	void Shutdown ()
+	{
+		Application.Shutdown ();
+	}
 
-		[Fact]
-		public void Init_Shutdown_Cleans_Up ()
-		{
-			// Verify initial state is per spec
-			//Pre_Init_State ();
+	[Fact]
+	public void Init_Shutdown_Cleans_Up ()
+	{
+		// Verify initial state is per spec
+		//Pre_Init_State ();
 
-			Application.Init (new FakeDriver ());
+		Application.Init (new FakeDriver ());
 
-			// Verify post-Init state is correct
-			//Post_Init_State ();
+		// Verify post-Init state is correct
+		//Post_Init_State ();
 
-			Application.Shutdown ();
+		Application.Shutdown ();
 
-			// Verify state is back to initial
-			//Pre_Init_State ();
+		// Verify state is back to initial
+		//Pre_Init_State ();
 #if DEBUG_IDISPOSABLE
 			// Validate there are no outstanding Responder-based instances 
 			// after a scenario was selected to run. This proves the main UI Catalog
 			// 'app' closed cleanly.
 			Assert.Empty (Responder.Instances);
 #endif
-		}
+	}
 
-		[Fact]
-		public void Init_Unbalanced_Throws ()
-		{
-			Application.Init (new FakeDriver ());
+	[Fact]
+	public void Init_Unbalanced_Throws ()
+	{
+		Application.Init (new FakeDriver ());
 
-			Toplevel topLevel = null;
-			Assert.Throws<InvalidOperationException> (() => Application.InternalInit (() => topLevel = new TestToplevel (), new FakeDriver ()));
-			Shutdown ();
+		Toplevel topLevel = null;
+		Assert.Throws<InvalidOperationException> (() => Application.InternalInit (() => topLevel = new TestToplevel (), new FakeDriver ()));
+		Shutdown ();
 
-			Assert.Null (Application.Top);
-			Assert.Null (Application.MainLoop);
-			Assert.Null (Application.Driver);
+		Assert.Null (Application.Top);
+		Assert.Null (Application.MainLoop);
+		Assert.Null (Application.Driver);
 
-			// Now try the other way
-			topLevel = null;
-			Application.InternalInit (() => topLevel = new TestToplevel (), new FakeDriver ());
+		// Now try the other way
+		topLevel = null;
+		Application.InternalInit (() => topLevel = new TestToplevel (), new FakeDriver ());
 
-			Assert.Throws<InvalidOperationException> (() => Application.Init (new FakeDriver ()));
-			Shutdown ();
+		Assert.Throws<InvalidOperationException> (() => Application.Init (new FakeDriver ()));
+		Shutdown ();
 
-			Assert.Null (Application.Top);
-			Assert.Null (Application.MainLoop);
-			Assert.Null (Application.Driver);
-		}
+		Assert.Null (Application.Top);
+		Assert.Null (Application.MainLoop);
+		Assert.Null (Application.Driver);
+	}
 
-		class TestToplevel : Toplevel {
-			public TestToplevel ()
-			{
-				IsOverlappedContainer = false;
-			}
+	class TestToplevel : Toplevel {
+		public TestToplevel ()
+		{
+			IsOverlappedContainer = false;
 		}
+	}
 
-		[Fact]
-		public void Init_Null_Driver_Should_Pick_A_Driver ()
-		{
-			Application.Init (null);
+	[Fact]
+	public void Init_Null_Driver_Should_Pick_A_Driver ()
+	{
+		Application.Init (null);
 
-			Assert.NotNull (Application.Driver);
+		Assert.NotNull (Application.Driver);
 
-			Shutdown ();
-		}
+		Shutdown ();
+	}
 
-		[Fact]
-		public void Init_Begin_End_Cleans_Up ()
-		{
-			Init ();
+	[Fact]
+	public void Init_Begin_End_Cleans_Up ()
+	{
+		Init ();
+
+		// Begin will cause Run() to be called, which will call Begin(). Thus will block the tests
+		// if we don't stop
+		Application.Iteration += (s, a) => {
+			Application.RequestStop ();
+		};
+
+		RunState runstate = null;
+		EventHandler<RunStateEventArgs> NewRunStateFn = (s, e) => {
+			Assert.NotNull (e.State);
+			runstate = e.State;
+		};
+		Application.NotifyNewRunState += NewRunStateFn;
+
+		Toplevel topLevel = new Toplevel ();
+		var rs = Application.Begin (topLevel);
+		Assert.NotNull (rs);
+		Assert.NotNull (runstate);
+		Assert.Equal (rs, runstate);
+
+		Assert.Equal (topLevel, Application.Top);
+		Assert.Equal (topLevel, Application.Current);
+
+		Application.NotifyNewRunState -= NewRunStateFn;
+		Application.End (runstate);
+
+		Assert.Null (Application.Current);
+		Assert.NotNull (Application.Top);
+		Assert.NotNull (Application.MainLoop);
+		Assert.NotNull (Application.Driver);
+
+		Shutdown ();
+
+		Assert.Null (Application.Top);
+		Assert.Null (Application.MainLoop);
+		Assert.Null (Application.Driver);
+	}
 
-			// Begin will cause Run() to be called, which will call Begin(). Thus will block the tests
-			// if we don't stop
-			Application.Iteration = () => {
-				Application.RequestStop ();
-			};
+	[Fact]
+	public void InitWithTopLevelFactory_Begin_End_Cleans_Up ()
+	{
+		// Begin will cause Run() to be called, which will call Begin(). Thus will block the tests
+		// if we don't stop
+		Application.Iteration += (s, a) => {
+			Application.RequestStop ();
+		};
+
+		// NOTE: Run<T>, when called after Init has been called behaves differently than
+		// when called if Init has not been called.
+		Toplevel topLevel = null;
+		Application.InternalInit (() => topLevel = new TestToplevel (), new FakeDriver ());
+
+		RunState runstate = null;
+		EventHandler<RunStateEventArgs> NewRunStateFn = (s, e) => {
+			Assert.NotNull (e.State);
+			runstate = e.State;
+		};
+		Application.NotifyNewRunState += NewRunStateFn;
+
+		var rs = Application.Begin (topLevel);
+		Assert.NotNull (rs);
+		Assert.NotNull (runstate);
+		Assert.Equal (rs, runstate);
+
+		Assert.Equal (topLevel, Application.Top);
+		Assert.Equal (topLevel, Application.Current);
+
+		Application.NotifyNewRunState -= NewRunStateFn;
+		Application.End (runstate);
+
+		Assert.Null (Application.Current);
+		Assert.NotNull (Application.Top);
+		Assert.NotNull (Application.MainLoop);
+		Assert.NotNull (Application.Driver);
+
+		Shutdown ();
+
+		Assert.Null (Application.Top);
+		Assert.Null (Application.MainLoop);
+		Assert.Null (Application.Driver);
+	}
 
-			RunState runstate = null;
-			EventHandler<RunStateEventArgs> NewRunStateFn = (s, e) => {
-				Assert.NotNull (e.State);
-				runstate = e.State;
-			};
-			Application.NotifyNewRunState += NewRunStateFn;
+	[Fact]
+	public void Begin_Null_Toplevel_Throws ()
+	{
+		// Setup Mock driver
+		Init ();
 
-			Toplevel topLevel = new Toplevel ();
-			var rs = Application.Begin (topLevel);
-			Assert.NotNull (rs);
-			Assert.NotNull (runstate);
-			Assert.Equal (rs, runstate);
+		// Test null Toplevel
+		Assert.Throws<ArgumentNullException> (() => Application.Begin (null));
 
-			Assert.Equal (topLevel, Application.Top);
-			Assert.Equal (topLevel, Application.Current);
+		Shutdown ();
 
-			Application.NotifyNewRunState -= NewRunStateFn;
-			Application.End (runstate);
+		Assert.Null (Application.Top);
+		Assert.Null (Application.MainLoop);
+		Assert.Null (Application.Driver);
+	}
 
-			Assert.Null (Application.Current);
-			Assert.NotNull (Application.Top);
-			Assert.NotNull (Application.MainLoop);
-			Assert.NotNull (Application.Driver);
+	#region RunTests
 
-			Shutdown ();
+	[Fact]
+	public void Run_T_After_InitWithDriver_with_TopLevel_Throws ()
+	{
+		// Setup Mock driver
+		Init ();
 
-			Assert.Null (Application.Top);
-			Assert.Null (Application.MainLoop);
-			Assert.Null (Application.Driver);
-		}
+		// Run<Toplevel> when already initialized with a Driver will throw (because Toplevel is not dervied from TopLevel)
+		Assert.Throws<ArgumentException> (() => Application.Run<Toplevel> (errorHandler: null));
 
-		[Fact]
-		public void InitWithTopLevelFactory_Begin_End_Cleans_Up ()
-		{
-			// Begin will cause Run() to be called, which will call Begin(). Thus will block the tests
-			// if we don't stop
-			Application.Iteration = () => {
-				Application.RequestStop ();
-			};
+		Shutdown ();
 
-			// NOTE: Run<T>, when called after Init has been called behaves differently than
-			// when called if Init has not been called.
-			Toplevel topLevel = null;
-			Application.InternalInit (() => topLevel = new TestToplevel (), new FakeDriver ());
+		Assert.Null (Application.Top);
+		Assert.Null (Application.MainLoop);
+		Assert.Null (Application.Driver);
+	}
 
-			RunState runstate = null;
-			EventHandler<RunStateEventArgs> NewRunStateFn = (s, e) => {
-				Assert.NotNull (e.State);
-				runstate = e.State;
-			};
-			Application.NotifyNewRunState += NewRunStateFn;
+	[Fact]
+	public void Run_T_After_InitWithDriver_with_TopLevel_and_Driver_Throws ()
+	{
+		// Setup Mock driver
+		Init ();
 
-			var rs = Application.Begin (topLevel);
-			Assert.NotNull (rs);
-			Assert.NotNull (runstate);
-			Assert.Equal (rs, runstate);
+		// Run<Toplevel> when already initialized with a Driver will throw (because Toplevel is not dervied from TopLevel)
+		Assert.Throws<ArgumentException> (() => Application.Run<Toplevel> (errorHandler: null, new FakeDriver ()));
 
-			Assert.Equal (topLevel, Application.Top);
-			Assert.Equal (topLevel, Application.Current);
+		Shutdown ();
 
-			Application.NotifyNewRunState -= NewRunStateFn;
-			Application.End (runstate);
+		Assert.Null (Application.Top);
+		Assert.Null (Application.MainLoop);
+		Assert.Null (Application.Driver);
+	}
 
-			Assert.Null (Application.Current);
-			Assert.NotNull (Application.Top);
-			Assert.NotNull (Application.MainLoop);
-			Assert.NotNull (Application.Driver);
+	[Fact]
+	public void Run_T_After_InitWithDriver_with_TestTopLevel_DoesNotThrow ()
+	{
+		// Setup Mock driver
+		Init ();
 
-			Shutdown ();
+		Application.Iteration += (s, a) => {
+			Application.RequestStop ();
+		};
 
-			Assert.Null (Application.Top);
-			Assert.Null (Application.MainLoop);
-			Assert.Null (Application.Driver);
-		}
+		// Init has been called and we're passing no driver to Run<TestTopLevel>. This is ok.
+		Application.Run<TestToplevel> ();
 
-		[Fact]
-		public void Begin_Null_Toplevel_Throws ()
-		{
-			// Setup Mock driver
-			Init ();
+		Shutdown ();
 
-			// Test null Toplevel
-			Assert.Throws<ArgumentNullException> (() => Application.Begin (null));
+		Assert.Null (Application.Top);
+		Assert.Null (Application.MainLoop);
+		Assert.Null (Application.Driver);
+	}
 
-			Shutdown ();
+	[Fact]
+	public void Run_T_After_InitNullDriver_with_TestTopLevel_Throws ()
+	{
+		Application._forceFakeConsole = true;
 
-			Assert.Null (Application.Top);
-			Assert.Null (Application.MainLoop);
-			Assert.Null (Application.Driver);
-		}
+		Application.Init (null);
+		Assert.Equal (typeof (FakeDriver), Application.Driver.GetType ());
 
-		#region RunTests
+		Application.Iteration += (s, a) => {
+			Application.RequestStop ();
+		};
 
-		[Fact]
-		public void Run_T_After_InitWithDriver_with_TopLevel_Throws ()
-		{
-			// Setup Mock driver
-			Init ();
+		// Init has been called without selecting a driver and we're passing no driver to Run<TestTopLevel>. Bad
+		Application.Run<TestToplevel> ();
 
-			// Run<Toplevel> when already initialized with a Driver will throw (because Toplevel is not dervied from TopLevel)
-			Assert.Throws<ArgumentException> (() => Application.Run<Toplevel> (errorHandler: null));
+		Shutdown ();
 
-			Shutdown ();
+		Assert.Null (Application.Top);
+		Assert.Null (Application.MainLoop);
+		Assert.Null (Application.Driver);
+	}
 
-			Assert.Null (Application.Top);
-			Assert.Null (Application.MainLoop);
-			Assert.Null (Application.Driver);
-		}
+	[Fact]
+	public void Run_T_Init_Driver_Cleared_with_TestTopLevel_Throws ()
+	{
+		Init ();
 
-		[Fact]
-		public void Run_T_After_InitWithDriver_with_TopLevel_and_Driver_Throws ()
-		{
-			// Setup Mock driver
-			Init ();
+		Application.Driver = null;
 
-			// Run<Toplevel> when already initialized with a Driver will throw (because Toplevel is not dervied from TopLevel)
-			Assert.Throws<ArgumentException> (() => Application.Run<Toplevel> (errorHandler: null, new FakeDriver ()));
+		Application.Iteration += (s, a) => {
+			Application.RequestStop ();
+		};
 
-			Shutdown ();
+		// Init has been called, but Driver has been set to null. Bad.
+		Assert.Throws<InvalidOperationException> (() => Application.Run<TestToplevel> ());
 
-			Assert.Null (Application.Top);
-			Assert.Null (Application.MainLoop);
-			Assert.Null (Application.Driver);
-		}
+		Shutdown ();
 
-		[Fact]
-		public void Run_T_After_InitWithDriver_with_TestTopLevel_DoesNotThrow ()
-		{
-			// Setup Mock driver
-			Init ();
+		Assert.Null (Application.Top);
+		Assert.Null (Application.MainLoop);
+		Assert.Null (Application.Driver);
+	}
 
-			Application.Iteration = () => {
-				Application.RequestStop ();
-			};
+	[Fact]
+	public void Run_T_NoInit_DoesNotThrow ()
+	{
+		Application._forceFakeConsole = true;
 
-			// Init has been called and we're passing no driver to Run<TestTopLevel>. This is ok.
-			Application.Run<TestToplevel> ();
+		Application.Iteration += (s, a) => {
+			Application.RequestStop ();
+		};
 
-			Shutdown ();
+		Application.Run<TestToplevel> ();
+		Assert.Equal (typeof (FakeDriver), Application.Driver.GetType ());
 
-			Assert.Null (Application.Top);
-			Assert.Null (Application.MainLoop);
-			Assert.Null (Application.Driver);
-		}
+		Shutdown ();
 
-		[Fact]
-		public void Run_T_After_InitNullDriver_with_TestTopLevel_Throws ()
-		{
-			Application._forceFakeConsole = true;
+		Assert.Null (Application.Top);
+		Assert.Null (Application.MainLoop);
+		Assert.Null (Application.Driver);
+	}
 
-			Application.Init (null, null);
-			Assert.Equal (typeof (FakeDriver), Application.Driver.GetType ());
+	[Fact]
+	public void Run_T_NoInit_WithDriver_DoesNotThrow ()
+	{
+		Application.Iteration += (s, a) => {
+			Application.RequestStop ();
+		};
 
-			Application.Iteration = () => {
-				Application.RequestStop ();
-			};
+		// Init has NOT been called and we're passing a valid driver to Run<TestTopLevel>. This is ok.
+		Application.Run<TestToplevel> (errorHandler: null, new FakeDriver ());
 
-			// Init has been called without selecting a driver and we're passing no driver to Run<TestTopLevel>. Bad
-			Application.Run<TestToplevel> ();
+		Shutdown ();
 
-			Shutdown ();
+		Assert.Null (Application.Top);
+		Assert.Null (Application.MainLoop);
+		Assert.Null (Application.Driver);
+	}
 
-			Assert.Null (Application.Top);
-			Assert.Null (Application.MainLoop);
-			Assert.Null (Application.Driver);
-		}
+	[Fact]
+	public void Run_RequestStop_Stops ()
+	{
+		// Setup Mock driver
+		Init ();
 
-		[Fact]
-		public void Run_T_Init_Driver_Cleared_with_TestTopLevel_Throws ()
-		{
-			Init ();
+		var top = new Toplevel ();
+		var rs = Application.Begin (top);
+		Assert.NotNull (rs);
+		Assert.Equal (top, Application.Current);
 
-			Application.Driver = null;
+		Application.Iteration += (s, a) => {
+			Application.RequestStop ();
+		};
 
-			Application.Iteration = () => {
-				Application.RequestStop ();
-			};
+		Application.Run (top);
 
-			// Init has been called, but Driver has been set to null. Bad.
-			Assert.Throws<InvalidOperationException> (() => Application.Run<TestToplevel> ());
+		Application.Shutdown ();
+		Assert.Null (Application.Current);
+		Assert.Null (Application.Top);
+		Assert.Null (Application.MainLoop);
+		Assert.Null (Application.Driver);
+	}
 
-			Shutdown ();
+	[Fact]
+	public void Run_RunningFalse_Stops ()
+	{
+		// Setup Mock driver
+		Init ();
 
-			Assert.Null (Application.Top);
-			Assert.Null (Application.MainLoop);
-			Assert.Null (Application.Driver);
-		}
+		var top = new Toplevel ();
+		var rs = Application.Begin (top);
+		Assert.NotNull (rs);
+		Assert.Equal (top, Application.Current);
 
-		[Fact]
-		public void Run_T_NoInit_DoesNotThrow ()
-		{
-			Application._forceFakeConsole = true;
+		Application.Iteration += (s, a) => {
+			top.Running = false;
+		};
 
-			Application.Iteration = () => {
-				Application.RequestStop ();
-			};
+		Application.Run (top);
 
-			Application.Run<TestToplevel> ();
-			Assert.Equal (typeof (FakeDriver), Application.Driver.GetType ());
+		Application.Shutdown ();
+		Assert.Null (Application.Current);
+		Assert.Null (Application.Top);
+		Assert.Null (Application.MainLoop);
+		Assert.Null (Application.Driver);
+	}
 
-			Shutdown ();
+	[Fact]
+	public void Run_Loaded_Ready_Unlodaded_Events ()
+	{
+		Init ();
+		var top = Application.Top;
+		var count = 0;
+		top.Loaded += (s, e) => count++;
+		top.Ready += (s, e) => count++;
+		top.Unloaded += (s, e) => count++;
+		Application.Iteration += (s, a) => Application.RequestStop ();
+		Application.Run ();
+		Application.Shutdown ();
+		Assert.Equal (3, count);
+	}
 
-			Assert.Null (Application.Top);
-			Assert.Null (Application.MainLoop);
-			Assert.Null (Application.Driver);
-		}
+	// TODO: Add tests for Run that test errorHandler
 
-		[Fact]
-		public void Run_T_NoInit_WithDriver_DoesNotThrow ()
+	#endregion
+
+	#region ShutdownTests
+	[Fact]
+	public void Shutdown_Allows_Async ()
+	{
+		static async Task TaskWithAsyncContinuation ()
 		{
-			Application.Iteration = () => {
-				Application.RequestStop ();
-			};
+			await Task.Yield ();
+			await Task.Yield ();
+		}
+
+		Init ();
+		Application.Shutdown ();
 
-			// Init has NOT been called and we're passing a valid driver to Run<TestTopLevel>. This is ok.
-			Application.Run<TestToplevel> (errorHandler: null, new FakeDriver ());
+		var task = TaskWithAsyncContinuation ();
+		Thread.Sleep (20);
+		Assert.True (task.IsCompletedSuccessfully);
+	}
 
-			Shutdown ();
+	[Fact]
+	public void Shutdown_Resets_SyncContext ()
+	{
+		Init ();
+		Application.Shutdown ();
+		Assert.Null (SynchronizationContext.Current);
+	}
+	#endregion
+
+	[Fact, AutoInitShutdown]
+	public void Begin_Sets_Application_Top_To_Console_Size ()
+	{
+		Assert.Equal (new Rect (0, 0, 80, 25), Application.Top.Frame);
+
+		((FakeDriver)Application.Driver).SetBufferSize (5, 5);
+		Application.Begin (Application.Top);
+		Assert.Equal (new Rect (0, 0, 80, 25), Application.Top.Frame);
+		((FakeDriver)Application.Driver).SetBufferSize (5, 5);
+		Assert.Equal (new Rect (0, 0, 5, 5), Application.Top.Frame);
+	}
 
-			Assert.Null (Application.Top);
-			Assert.Null (Application.MainLoop);
-			Assert.Null (Application.Driver);
-		}
+	[Fact]
+	[AutoInitShutdown]
+	public void SetCurrentAsTop_Run_A_Not_Modal_Toplevel_Make_It_The_Current_Application_Top ()
+	{
+		var t1 = new Toplevel ();
+		var t2 = new Toplevel ();
+		var t3 = new Toplevel ();
+		var d = new Dialog ();
+		var t4 = new Toplevel ();
 
-		[Fact]
-		public void Run_RequestStop_Stops ()
-		{
-			// Setup Mock driver
-			Init ();
+		// t1, t2, t3, d, t4
+		var iterations = 5;
+
+		t1.Ready += (s, e) => {
+			Assert.Equal (t1, Application.Top);
+			Application.Run (t2);
+		};
+		t2.Ready += (s, e) => {
+			Assert.Equal (t2, Application.Top);
+			Application.Run (t3);
+		};
+		t3.Ready += (s, e) => {
+			Assert.Equal (t3, Application.Top);
+			Application.Run (d);
+		};
+		d.Ready += (s, e) => {
+			Assert.Equal (t3, Application.Top);
+			Application.Run (t4);
+		};
+		t4.Ready += (s, e) => {
+			Assert.Equal (t4, Application.Top);
+			t4.RequestStop ();
+			d.RequestStop ();
+			t3.RequestStop ();
+			t2.RequestStop ();
+		};
+		// Now this will close the OverlappedContainer when all OverlappedChildren was closed
+		t2.Closed += (s, _) => {
+			t1.RequestStop ();
+		};
+		Application.Iteration += (s, a) => {
+			if (iterations == 5) {
+				// The Current still is t4 because Current.Running is false.
+				Assert.Equal (t4, Application.Current);
+				Assert.False (Application.Current.Running);
+				Assert.Equal (t4, Application.Top);
+			} else if (iterations == 4) {
+				// The Current is d and Current.Running is false.
+				Assert.Equal (d, Application.Current);
+				Assert.False (Application.Current.Running);
+				Assert.Equal (t4, Application.Top);
+			} else if (iterations == 3) {
+				// The Current is t3 and Current.Running is false.
+				Assert.Equal (t3, Application.Current);
+				Assert.False (Application.Current.Running);
+				Assert.Equal (t3, Application.Top);
+			} else if (iterations == 2) {
+				// The Current is t2 and Current.Running is false.
+				Assert.Equal (t2, Application.Current);
+				Assert.False (Application.Current.Running);
+				Assert.Equal (t2, Application.Top);
+			} else {
+				// The Current is t1.
+				Assert.Equal (t1, Application.Current);
+				Assert.False (Application.Current.Running);
+				Assert.Equal (t1, Application.Top);
+			}
+			iterations--;
+		};
 
-			var top = new Toplevel ();
-			var rs = Application.Begin (top);
-			Assert.NotNull (rs);
-			Assert.Equal (top, Application.Current);
+		Application.Run (t1);
 
-			Application.Iteration = () => {
-				Application.RequestStop ();
-			};
+		Assert.Equal (t1, Application.Top);
+	}
 
-			Application.Run (top);
+	[Fact]
+	[AutoInitShutdown]
+	public void Internal_Properties_Correct ()
+	{
+		Assert.True (Application._initialized);
+		Assert.NotNull (Application.Top);
+		var rs = Application.Begin (Application.Top);
+		Assert.Equal (Application.Top, rs.Toplevel);
+		Assert.Null (Application.MouseGrabView);  // public
+		Assert.Null (Application.WantContinuousButtonPressedView); // public
+		Assert.False (Application.MoveToOverlappedChild (Application.Top));
+	}
 
-			Application.Shutdown ();
-			Assert.Null (Application.Current);
-			Assert.Null (Application.Top);
-			Assert.Null (Application.MainLoop);
-			Assert.Null (Application.Driver);
+	#region KeyboardTests
+	[Fact]
+	public void KeyUp_Event ()
+	{
+		// Setup Mock driver
+		Init ();
+
+		// Setup some fake keypresses (This)
+		var input = "Tests";
+
+		// Put a control-q in at the end
+		FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo ('q', ConsoleKey.Q, shift: false, alt: false, control: true));
+		foreach (var c in input.Reverse ()) {
+			if (char.IsLetter (c)) {
+				FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo (c, (ConsoleKey)char.ToUpper (c), shift: char.IsUpper (c), alt: false, control: false));
+			} else {
+				FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo (c, (ConsoleKey)c, shift: false, alt: false, control: false));
+			}
 		}
 
-		[Fact]
-		public void Run_RunningFalse_Stops ()
-		{
-			// Setup Mock driver
-			Init ();
+		int stackSize = FakeConsole.MockKeyPresses.Count;
 
-			var top = new Toplevel ();
-			var rs = Application.Begin (top);
-			Assert.NotNull (rs);
-			Assert.Equal (top, Application.Current);
+		int iterations = 0;
+		Application.Iteration += (s, a) => {
+			iterations++;
+			// Stop if we run out of control...
+			if (iterations > 10) {
+				Application.RequestStop ();
+			}
+		};
 
-			Application.Iteration = () => {
-				top.Running = false;
-			};
+		int keyUps = 0;
+		var output = string.Empty;
+		Application.Top.KeyUp += (object sender, KeyEventEventArgs args) => {
+			if (args.KeyEvent.Key != (Key.CtrlMask | Key.Q)) {
+				output += (char)args.KeyEvent.KeyValue;
+			}
+			keyUps++;
+		};
 
-			Application.Run (top);
+		Application.Run (Application.Top);
 
-			Application.Shutdown ();
-			Assert.Null (Application.Current);
-			Assert.Null (Application.Top);
-			Assert.Null (Application.MainLoop);
-			Assert.Null (Application.Driver);
-		}
+		// Input string should match output
+		Assert.Equal (input, output);
 
-		[Fact]
-		public void Run_Loaded_Ready_Unlodaded_Events ()
-		{
-			Init ();
-			var top = Application.Top;
-			var count = 0;
-			top.Loaded += (s, e) => count++;
-			top.Ready += (s, e) => count++;
-			top.Unloaded += (s, e) => count++;
-			Application.Iteration = () => Application.RequestStop ();
-			Application.Run ();
-			Application.Shutdown ();
-			Assert.Equal (3, count);
-		}
+		// # of key up events should match stack size
+		//Assert.Equal (stackSize, keyUps);
+		// We can't use numbers variables on the left side of an Assert.Equal/NotEqual,
+		// it must be literal (Linux only).
+		Assert.Equal (6, keyUps);
 
-		// TODO: Add tests for Run that test errorHandler
+		// # of key up events should match # of iterations
+		Assert.Equal (stackSize, iterations);
 
-		#endregion
+		Application.Shutdown ();
+		Assert.Null (Application.Current);
+		Assert.Null (Application.Top);
+		Assert.Null (Application.MainLoop);
+		Assert.Null (Application.Driver);
+	}
 
-		#region ShutdownTests
-		[Fact]
-		public void Shutdown_Allows_Async ()
-		{
-			static async Task TaskWithAsyncContinuation ()
-			{
-				await Task.Yield ();
-				await Task.Yield ();
-			}
+	[Fact]
+	public void AlternateForwardKey_AlternateBackwardKey_Tests ()
+	{
+		Init ();
+
+		var top = Application.Top;
+		var w1 = new Window ();
+		var v1 = new TextField ();
+		var v2 = new TextView ();
+		w1.Add (v1, v2);
+
+		var w2 = new Window ();
+		var v3 = new CheckBox ();
+		var v4 = new Button ();
+		w2.Add (v3, v4);
+
+		top.Add (w1, w2);
+
+		Application.Iteration += (s, a) => {
+			Assert.True (v1.HasFocus);
+			// Using default keys.
+			top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.Tab,
+				new KeyModifiers () { Ctrl = true }));
+			Assert.True (v2.HasFocus);
+			top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.Tab,
+				new KeyModifiers () { Ctrl = true }));
+			Assert.True (v3.HasFocus);
+			top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.Tab,
+				new KeyModifiers () { Ctrl = true }));
+			Assert.True (v4.HasFocus);
+			top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.Tab,
+				new KeyModifiers () { Ctrl = true }));
+			Assert.True (v1.HasFocus);
+
+			top.ProcessKey (new KeyEvent (Key.ShiftMask | Key.CtrlMask | Key.Tab,
+				new KeyModifiers () { Shift = true, Ctrl = true }));
+			Assert.True (v4.HasFocus);
+			top.ProcessKey (new KeyEvent (Key.ShiftMask | Key.CtrlMask | Key.Tab,
+				new KeyModifiers () { Shift = true, Ctrl = true }));
+			Assert.True (v3.HasFocus);
+			top.ProcessKey (new KeyEvent (Key.ShiftMask | Key.CtrlMask | Key.Tab,
+				new KeyModifiers () { Shift = true, Ctrl = true }));
+			Assert.True (v2.HasFocus);
+			top.ProcessKey (new KeyEvent (Key.ShiftMask | Key.CtrlMask | Key.Tab,
+				new KeyModifiers () { Shift = true, Ctrl = true }));
+			Assert.True (v1.HasFocus);
+
+			top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.PageDown,
+				new KeyModifiers () { Ctrl = true }));
+			Assert.True (v2.HasFocus);
+			top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.PageDown,
+				new KeyModifiers () { Ctrl = true }));
+			Assert.True (v3.HasFocus);
+			top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.PageDown,
+				new KeyModifiers () { Ctrl = true }));
+			Assert.True (v4.HasFocus);
+			top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.PageDown,
+				new KeyModifiers () { Ctrl = true }));
+			Assert.True (v1.HasFocus);
+
+			top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.PageUp,
+				new KeyModifiers () { Ctrl = true }));
+			Assert.True (v4.HasFocus);
+			top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.PageUp,
+				new KeyModifiers () { Ctrl = true }));
+			Assert.True (v3.HasFocus);
+			top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.PageUp,
+				new KeyModifiers () { Ctrl = true }));
+			Assert.True (v2.HasFocus);
+			top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.PageUp,
+				new KeyModifiers () { Ctrl = true }));
+			Assert.True (v1.HasFocus);
+
+			// Using another's alternate keys.
+			Application.AlternateForwardKey = Key.F7;
+			Application.AlternateBackwardKey = Key.F6;
+
+			top.ProcessKey (new KeyEvent (Key.F7, new KeyModifiers ()));
+			Assert.True (v2.HasFocus);
+			top.ProcessKey (new KeyEvent (Key.F7, new KeyModifiers ()));
+			Assert.True (v3.HasFocus);
+			top.ProcessKey (new KeyEvent (Key.F7, new KeyModifiers ()));
+			Assert.True (v4.HasFocus);
+			top.ProcessKey (new KeyEvent (Key.F7, new KeyModifiers ()));
+			Assert.True (v1.HasFocus);
+
+			top.ProcessKey (new KeyEvent (Key.F6, new KeyModifiers ()));
+			Assert.True (v4.HasFocus);
+			top.ProcessKey (new KeyEvent (Key.F6, new KeyModifiers ()));
+			Assert.True (v3.HasFocus);
+			top.ProcessKey (new KeyEvent (Key.F6, new KeyModifiers ()));
+			Assert.True (v2.HasFocus);
+			top.ProcessKey (new KeyEvent (Key.F6, new KeyModifiers ()));
+			Assert.True (v1.HasFocus);
+
+			Application.RequestStop ();
+		};
+
+		Application.Run (top);
+
+		// Replacing the defaults keys to avoid errors on others unit tests that are using it.
+		Application.AlternateForwardKey = Key.PageDown | Key.CtrlMask;
+		Application.AlternateBackwardKey = Key.PageUp | Key.CtrlMask;
+		Application.QuitKey = Key.Q | Key.CtrlMask;
+
+		Assert.Equal (Key.PageDown | Key.CtrlMask, Application.AlternateForwardKey);
+		Assert.Equal (Key.PageUp | Key.CtrlMask, Application.AlternateBackwardKey);
+		Assert.Equal (Key.Q | Key.CtrlMask, Application.QuitKey);
+
+		// Shutdown must be called to safely clean up Application if Init has been called
+		Application.Shutdown ();
+	}
 
-			Init ();
-			Application.Shutdown ();
+	[Fact]
+	[AutoInitShutdown]
+	public void QuitKey_Getter_Setter ()
+	{
+		var top = Application.Top;
+		var isQuiting = false;
 
-			var task = TaskWithAsyncContinuation ();
-			Thread.Sleep (20);
-			Assert.True (task.IsCompletedSuccessfully);
-		}
+		top.Closing += (s, e) => {
+			isQuiting = true;
+			e.Cancel = true;
+		};
 
-		[Fact]
-		public void Shutdown_Resets_SyncContext ()
-		{
-			Init ();
-			Application.Shutdown ();
-			Assert.Null (SynchronizationContext.Current);
-		}
-		#endregion
+		Application.Begin (top);
+		top.Running = true;
 
-		[Fact, AutoInitShutdown]
-		public void Begin_Sets_Application_Top_To_Console_Size ()
-		{
-			Assert.Equal (new Rect (0, 0, 80, 25), Application.Top.Frame);
+		Assert.Equal (Key.Q | Key.CtrlMask, Application.QuitKey);
+		Application.Driver.SendKeys ('q', ConsoleKey.Q, false, false, true);
+		Assert.True (isQuiting);
 
-			((FakeDriver)Application.Driver).SetBufferSize (5, 5);
-			Application.Begin (Application.Top);
-			Assert.Equal (new Rect (0, 0, 80, 25), Application.Top.Frame);
-			((FakeDriver)Application.Driver).SetBufferSize (5, 5);
-			Assert.Equal (new Rect (0, 0, 5, 5), Application.Top.Frame);
-		}
+		isQuiting = false;
+		Application.QuitKey = Key.C | Key.CtrlMask;
 
-		[Fact]
-		[AutoInitShutdown]
-		public void SetCurrentAsTop_Run_A_Not_Modal_Toplevel_Make_It_The_Current_Application_Top ()
-		{
-			var t1 = new Toplevel ();
-			var t2 = new Toplevel ();
-			var t3 = new Toplevel ();
-			var d = new Dialog ();
-			var t4 = new Toplevel ();
+		Application.Driver.SendKeys ('q', ConsoleKey.Q, false, false, true);
+		Assert.False (isQuiting);
+		Application.Driver.SendKeys ('c', ConsoleKey.C, false, false, true);
+		Assert.True (isQuiting);
 
-			// t1, t2, t3, d, t4
-			var iterations = 5;
+		// Reset the QuitKey to avoid throws errors on another tests
+		Application.QuitKey = Key.Q | Key.CtrlMask;
+	}
 
-			t1.Ready += (s, e) => {
-				Assert.Equal (t1, Application.Top);
-				Application.Run (t2);
-			};
-			t2.Ready += (s, e) => {
-				Assert.Equal (t2, Application.Top);
-				Application.Run (t3);
-			};
-			t3.Ready += (s, e) => {
-				Assert.Equal (t3, Application.Top);
-				Application.Run (d);
-			};
-			d.Ready += (s, e) => {
-				Assert.Equal (t3, Application.Top);
-				Application.Run (t4);
-			};
-			t4.Ready += (s, e) => {
-				Assert.Equal (t4, Application.Top);
-				t4.RequestStop ();
-				d.RequestStop ();
-				t3.RequestStop ();
-				t2.RequestStop ();
-			};
-			// Now this will close the OverlappedContainer when all OverlappedChildren was closed
-			t2.Closed += (s, _) => {
-				t1.RequestStop ();
-			};
-			Application.Iteration += () => {
-				if (iterations == 5) {
-					// The Current still is t4 because Current.Running is false.
-					Assert.Equal (t4, Application.Current);
-					Assert.False (Application.Current.Running);
-					Assert.Equal (t4, Application.Top);
-				} else if (iterations == 4) {
-					// The Current is d and Current.Running is false.
-					Assert.Equal (d, Application.Current);
-					Assert.False (Application.Current.Running);
-					Assert.Equal (t4, Application.Top);
-				} else if (iterations == 3) {
-					// The Current is t3 and Current.Running is false.
-					Assert.Equal (t3, Application.Current);
-					Assert.False (Application.Current.Running);
-					Assert.Equal (t3, Application.Top);
-				} else if (iterations == 2) {
-					// The Current is t2 and Current.Running is false.
-					Assert.Equal (t2, Application.Current);
-					Assert.False (Application.Current.Running);
-					Assert.Equal (t2, Application.Top);
-				} else {
-					// The Current is t1.
-					Assert.Equal (t1, Application.Current);
-					Assert.False (Application.Current.Running);
-					Assert.Equal (t1, Application.Top);
-				}
-				iterations--;
-			};
-
-			Application.Run (t1);
+	[Fact]
+	[AutoInitShutdown]
+	public void EnsuresTopOnFront_CanFocus_True_By_Keyboard_And_Mouse ()
+	{
+		var top = Application.Top;
+		var win = new Window () { Title = "win", X = 0, Y = 0, Width = 20, Height = 10 };
+		var tf = new TextField () { Width = 10 };
+		win.Add (tf);
+		var win2 = new Window () { Title = "win2", X = 22, Y = 0, Width = 20, Height = 10 };
+		var tf2 = new TextField () { Width = 10 };
+		win2.Add (tf2);
+		top.Add (win, win2);
+
+		Application.Begin (top);
+
+		Assert.True (win.CanFocus);
+		Assert.True (win.HasFocus);
+		Assert.True (win2.CanFocus);
+		Assert.False (win2.HasFocus);
+		Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
+
+		top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.Tab, new KeyModifiers ()));
+		Assert.True (win.CanFocus);
+		Assert.False (win.HasFocus);
+		Assert.True (win2.CanFocus);
+		Assert.True (win2.HasFocus);
+		Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
+
+		top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.Tab, new KeyModifiers ()));
+		Assert.True (win.CanFocus);
+		Assert.True (win.HasFocus);
+		Assert.True (win2.CanFocus);
+		Assert.False (win2.HasFocus);
+		Assert.Equal ("win", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
+
+		win2.MouseEvent (new MouseEvent () { Flags = MouseFlags.Button1Pressed });
+		Assert.True (win.CanFocus);
+		Assert.False (win.HasFocus);
+		Assert.True (win2.CanFocus);
+		Assert.True (win2.HasFocus);
+		Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
+		win2.MouseEvent (new MouseEvent () { Flags = MouseFlags.Button1Released });
+		Assert.Null (Toplevel._dragPosition);
+	}
 
-			Assert.Equal (t1, Application.Top);
-		}
+	[Fact]
+	[AutoInitShutdown]
+	public void EnsuresTopOnFront_CanFocus_False_By_Keyboard_And_Mouse ()
+	{
+		var top = Application.Top;
+		var win = new Window () { Title = "win", X = 0, Y = 0, Width = 20, Height = 10 };
+		var tf = new TextField () { Width = 10 };
+		win.Add (tf);
+		var win2 = new Window () { Title = "win2", X = 22, Y = 0, Width = 20, Height = 10 };
+		var tf2 = new TextField () { Width = 10 };
+		win2.Add (tf2);
+		top.Add (win, win2);
+
+		Application.Begin (top);
+
+		Assert.True (win.CanFocus);
+		Assert.True (win.HasFocus);
+		Assert.True (win2.CanFocus);
+		Assert.False (win2.HasFocus);
+		Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
+
+		win.CanFocus = false;
+		Assert.False (win.CanFocus);
+		Assert.False (win.HasFocus);
+		Assert.True (win2.CanFocus);
+		Assert.True (win2.HasFocus);
+		Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
+
+		top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.Tab, new KeyModifiers ()));
+		Assert.True (win2.CanFocus);
+		Assert.False (win.HasFocus);
+		Assert.True (win2.CanFocus);
+		Assert.True (win2.HasFocus);
+		Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
+
+		top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.Tab, new KeyModifiers ()));
+		Assert.False (win.CanFocus);
+		Assert.False (win.HasFocus);
+		Assert.True (win2.CanFocus);
+		Assert.True (win2.HasFocus);
+		Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
+
+		win.MouseEvent (new MouseEvent () { Flags = MouseFlags.Button1Pressed });
+		Assert.False (win.CanFocus);
+		Assert.False (win.HasFocus);
+		Assert.True (win2.CanFocus);
+		Assert.True (win2.HasFocus);
+		Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
+		win2.MouseEvent (new MouseEvent () { Flags = MouseFlags.Button1Released });
+		Assert.Null (Toplevel._dragPosition);
+	}
 
-		[Fact]
-		[AutoInitShutdown]
-		public void Internal_Properties_Correct ()
-		{
-			Assert.True (Application._initialized);
-			Assert.NotNull (Application.Top);
-			var rs = Application.Begin (Application.Top);
-			Assert.Equal (Application.Top, rs.Toplevel);
-			Assert.Null (Application.MouseGrabView);  // public
-			Assert.Null (Application.WantContinuousButtonPressedView); // public
-			Assert.False (Application.MoveToOverlappedChild (Application.Top));
-		}
+	#endregion
 
-		#region KeyboardTests
-		[Fact]
-		public void KeyUp_Event ()
-		{
-			// Setup Mock driver
-			Init ();
-
-			// Setup some fake keypresses (This)
-			var input = "Tests";
-
-			// Put a control-q in at the end
-			FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo ('q', ConsoleKey.Q, shift: false, alt: false, control: true));
-			foreach (var c in input.Reverse ()) {
-				if (char.IsLetter (c)) {
-					FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo (c, (ConsoleKey)char.ToUpper (c), shift: char.IsUpper (c), alt: false, control: false));
-				} else {
-					FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo (c, (ConsoleKey)c, shift: false, alt: false, control: false));
-				}
-			}
 
-			int stackSize = FakeConsole.MockKeyPresses.Count;
-
-			int iterations = 0;
-			Application.Iteration = () => {
-				iterations++;
-				// Stop if we run out of control...
-				if (iterations > 10) {
-					Application.RequestStop ();
-				}
-			};
-
-			int keyUps = 0;
-			var output = string.Empty;
-			Application.Top.KeyUp += (object sender, KeyEventEventArgs args) => {
-				if (args.KeyEvent.Key != (Key.CtrlMask | Key.Q)) {
-					output += (char)args.KeyEvent.KeyValue;
-				}
-				keyUps++;
-			};
-
-			Application.Run (Application.Top);
-
-			// Input string should match output
-			Assert.Equal (input, output);
-
-			// # of key up events should match stack size
-			//Assert.Equal (stackSize, keyUps);
-			// We can't use numbers variables on the left side of an Assert.Equal/NotEqual,
-			// it must be literal (Linux only).
-			Assert.Equal (6, keyUps);
-
-			// # of key up events should match # of iterations
-			Assert.Equal (stackSize, iterations);
-
-			Application.Shutdown ();
-			Assert.Null (Application.Current);
-			Assert.Null (Application.Top);
-			Assert.Null (Application.MainLoop);
-			Assert.Null (Application.Driver);
-		}
+	// Invoke Tests
+	// TODO: Test with threading scenarios
+	[Fact]
+	public void Invoke_Adds_Idle ()
+	{
+		Application.Init (new FakeDriver ());
+		var top = new Toplevel ();
+		var rs = Application.Begin (top);
+		bool firstIteration = false;
 
-		[Fact]
-		public void AlternateForwardKey_AlternateBackwardKey_Tests ()
-		{
-			Init ();
-
-			var top = Application.Top;
-			var w1 = new Window ();
-			var v1 = new TextField ();
-			var v2 = new TextView ();
-			w1.Add (v1, v2);
-
-			var w2 = new Window ();
-			var v3 = new CheckBox ();
-			var v4 = new Button ();
-			w2.Add (v3, v4);
-
-			top.Add (w1, w2);
-
-			Application.Iteration += () => {
-				Assert.True (v1.HasFocus);
-				// Using default keys.
-				top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.Tab,
-					new KeyModifiers () { Ctrl = true }));
-				Assert.True (v2.HasFocus);
-				top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.Tab,
-					new KeyModifiers () { Ctrl = true }));
-				Assert.True (v3.HasFocus);
-				top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.Tab,
-					new KeyModifiers () { Ctrl = true }));
-				Assert.True (v4.HasFocus);
-				top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.Tab,
-					new KeyModifiers () { Ctrl = true }));
-				Assert.True (v1.HasFocus);
-
-				top.ProcessKey (new KeyEvent (Key.ShiftMask | Key.CtrlMask | Key.Tab,
-					new KeyModifiers () { Shift = true, Ctrl = true }));
-				Assert.True (v4.HasFocus);
-				top.ProcessKey (new KeyEvent (Key.ShiftMask | Key.CtrlMask | Key.Tab,
-					new KeyModifiers () { Shift = true, Ctrl = true }));
-				Assert.True (v3.HasFocus);
-				top.ProcessKey (new KeyEvent (Key.ShiftMask | Key.CtrlMask | Key.Tab,
-					new KeyModifiers () { Shift = true, Ctrl = true }));
-				Assert.True (v2.HasFocus);
-				top.ProcessKey (new KeyEvent (Key.ShiftMask | Key.CtrlMask | Key.Tab,
-					new KeyModifiers () { Shift = true, Ctrl = true }));
-				Assert.True (v1.HasFocus);
-
-				top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.PageDown,
-					new KeyModifiers () { Ctrl = true }));
-				Assert.True (v2.HasFocus);
-				top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.PageDown,
-					new KeyModifiers () { Ctrl = true }));
-				Assert.True (v3.HasFocus);
-				top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.PageDown,
-					new KeyModifiers () { Ctrl = true }));
-				Assert.True (v4.HasFocus);
-				top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.PageDown,
-					new KeyModifiers () { Ctrl = true }));
-				Assert.True (v1.HasFocus);
-
-				top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.PageUp,
-					new KeyModifiers () { Ctrl = true }));
-				Assert.True (v4.HasFocus);
-				top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.PageUp,
-					new KeyModifiers () { Ctrl = true }));
-				Assert.True (v3.HasFocus);
-				top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.PageUp,
-					new KeyModifiers () { Ctrl = true }));
-				Assert.True (v2.HasFocus);
-				top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.PageUp,
-					new KeyModifiers () { Ctrl = true }));
-				Assert.True (v1.HasFocus);
-
-				// Using another's alternate keys.
-				Application.AlternateForwardKey = Key.F7;
-				Application.AlternateBackwardKey = Key.F6;
-
-				top.ProcessKey (new KeyEvent (Key.F7, new KeyModifiers ()));
-				Assert.True (v2.HasFocus);
-				top.ProcessKey (new KeyEvent (Key.F7, new KeyModifiers ()));
-				Assert.True (v3.HasFocus);
-				top.ProcessKey (new KeyEvent (Key.F7, new KeyModifiers ()));
-				Assert.True (v4.HasFocus);
-				top.ProcessKey (new KeyEvent (Key.F7, new KeyModifiers ()));
-				Assert.True (v1.HasFocus);
-
-				top.ProcessKey (new KeyEvent (Key.F6, new KeyModifiers ()));
-				Assert.True (v4.HasFocus);
-				top.ProcessKey (new KeyEvent (Key.F6, new KeyModifiers ()));
-				Assert.True (v3.HasFocus);
-				top.ProcessKey (new KeyEvent (Key.F6, new KeyModifiers ()));
-				Assert.True (v2.HasFocus);
-				top.ProcessKey (new KeyEvent (Key.F6, new KeyModifiers ()));
-				Assert.True (v1.HasFocus);
+		var actionCalled = 0;
+		Application.Invoke (() => { actionCalled++; });
+		Application.RunIteration (ref rs, ref firstIteration);
+		Assert.Equal (1, actionCalled);
+		Application.Shutdown ();
+	}
 
-				Application.RequestStop ();
-			};
 
-			Application.Run (top);
+	#region mousegrabtests
+	[Fact, AutoInitShutdown]
+	public void MouseGrabView_WithNullMouseEventView ()
+	{
+		var tf = new TextField () { Width = 10 };
+		var sv = new ScrollView () {
+			Width = Dim.Fill (),
+			Height = Dim.Fill (),
+			ContentSize = new Size (100, 100)
+		};
 
-			// Replacing the defaults keys to avoid errors on others unit tests that are using it.
-			Application.AlternateForwardKey = Key.PageDown | Key.CtrlMask;
-			Application.AlternateBackwardKey = Key.PageUp | Key.CtrlMask;
-			Application.QuitKey = Key.Q | Key.CtrlMask;
+		sv.Add (tf);
+		Application.Top.Add (sv);
 
-			Assert.Equal (Key.PageDown | Key.CtrlMask, Application.AlternateForwardKey);
-			Assert.Equal (Key.PageUp | Key.CtrlMask, Application.AlternateBackwardKey);
-			Assert.Equal (Key.Q | Key.CtrlMask, Application.QuitKey);
+		var iterations = -1;
 
-			// Shutdown must be called to safely clean up Application if Init has been called
-			Application.Shutdown ();
-		}
+		Application.Iteration += (s, a) => {
+			iterations++;
+			if (iterations == 0) {
+				Assert.True (tf.HasFocus);
+				Assert.Null (Application.MouseGrabView);
 
-		[Fact]
-		[AutoInitShutdown]
-		public void QuitKey_Getter_Setter ()
-		{
-			var top = Application.Top;
-			var isQuiting = false;
+				Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+					X = 5,
+					Y = 5,
+					Flags = MouseFlags.ReportMousePosition
+				}));
 
-			top.Closing += (s, e) => {
-				isQuiting = true;
-				e.Cancel = true;
-			};
+				Assert.Equal (sv, Application.MouseGrabView);
 
-			Application.Begin (top);
-			top.Running = true;
+				MessageBox.Query ("Title", "Test", "Ok");
 
-			Assert.Equal (Key.Q | Key.CtrlMask, Application.QuitKey);
-			Application.Driver.SendKeys ('q', ConsoleKey.Q, false, false, true);
-			Assert.True (isQuiting);
+				Assert.Null (Application.MouseGrabView);
+			} else if (iterations == 1) {
+				// Application.MouseGrabView is null because
+				// another toplevel (Dialog) was opened
+				Assert.Null (Application.MouseGrabView);
 
-			isQuiting = false;
-			Application.QuitKey = Key.C | Key.CtrlMask;
+				Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+					X = 5,
+					Y = 5,
+					Flags = MouseFlags.ReportMousePosition
+				}));
 
-			Application.Driver.SendKeys ('q', ConsoleKey.Q, false, false, true);
-			Assert.False (isQuiting);
-			Application.Driver.SendKeys ('c', ConsoleKey.C, false, false, true);
-			Assert.True (isQuiting);
+				Assert.Null (Application.MouseGrabView);
 
-			// Reset the QuitKey to avoid throws errors on another tests
-			Application.QuitKey = Key.Q | Key.CtrlMask;
-		}
+				Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+					X = 40,
+					Y = 12,
+					Flags = MouseFlags.ReportMousePosition
+				}));
 
-		[Fact]
-		[AutoInitShutdown]
-		public void EnsuresTopOnFront_CanFocus_True_By_Keyboard_And_Mouse ()
-		{
-			var top = Application.Top;
-			var win = new Window () { Title = "win", X = 0, Y = 0, Width = 20, Height = 10 };
-			var tf = new TextField () { Width = 10 };
-			win.Add (tf);
-			var win2 = new Window () { Title = "win2", X = 22, Y = 0, Width = 20, Height = 10 };
-			var tf2 = new TextField () { Width = 10 };
-			win2.Add (tf2);
-			top.Add (win, win2);
-
-			Application.Begin (top);
-
-			Assert.True (win.CanFocus);
-			Assert.True (win.HasFocus);
-			Assert.True (win2.CanFocus);
-			Assert.False (win2.HasFocus);
-			Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
-
-			top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.Tab, new KeyModifiers ()));
-			Assert.True (win.CanFocus);
-			Assert.False (win.HasFocus);
-			Assert.True (win2.CanFocus);
-			Assert.True (win2.HasFocus);
-			Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
-
-			top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.Tab, new KeyModifiers ()));
-			Assert.True (win.CanFocus);
-			Assert.True (win.HasFocus);
-			Assert.True (win2.CanFocus);
-			Assert.False (win2.HasFocus);
-			Assert.Equal ("win", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
-
-			win2.MouseEvent (new MouseEvent () { Flags = MouseFlags.Button1Pressed });
-			Assert.True (win.CanFocus);
-			Assert.False (win.HasFocus);
-			Assert.True (win2.CanFocus);
-			Assert.True (win2.HasFocus);
-			Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
-			win2.MouseEvent (new MouseEvent () { Flags = MouseFlags.Button1Released });
-			Assert.Null (Toplevel._dragPosition);
-		}
+				Assert.Null (Application.MouseGrabView);
 
-		[Fact]
-		[AutoInitShutdown]
-		public void EnsuresTopOnFront_CanFocus_False_By_Keyboard_And_Mouse ()
-		{
-			var top = Application.Top;
-			var win = new Window () { Title = "win", X = 0, Y = 0, Width = 20, Height = 10 };
-			var tf = new TextField () { Width = 10 };
-			win.Add (tf);
-			var win2 = new Window () { Title = "win2", X = 22, Y = 0, Width = 20, Height = 10 };
-			var tf2 = new TextField () { Width = 10 };
-			win2.Add (tf2);
-			top.Add (win, win2);
-
-			Application.Begin (top);
-
-			Assert.True (win.CanFocus);
-			Assert.True (win.HasFocus);
-			Assert.True (win2.CanFocus);
-			Assert.False (win2.HasFocus);
-			Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
-
-			win.CanFocus = false;
-			Assert.False (win.CanFocus);
-			Assert.False (win.HasFocus);
-			Assert.True (win2.CanFocus);
-			Assert.True (win2.HasFocus);
-			Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
-
-			top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.Tab, new KeyModifiers ()));
-			Assert.True (win2.CanFocus);
-			Assert.False (win.HasFocus);
-			Assert.True (win2.CanFocus);
-			Assert.True (win2.HasFocus);
-			Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
-
-			top.ProcessKey (new KeyEvent (Key.CtrlMask | Key.Tab, new KeyModifiers ()));
-			Assert.False (win.CanFocus);
-			Assert.False (win.HasFocus);
-			Assert.True (win2.CanFocus);
-			Assert.True (win2.HasFocus);
-			Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
-
-			win.MouseEvent (new MouseEvent () { Flags = MouseFlags.Button1Pressed });
-			Assert.False (win.CanFocus);
-			Assert.False (win.HasFocus);
-			Assert.True (win2.CanFocus);
-			Assert.True (win2.HasFocus);
-			Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
-			win2.MouseEvent (new MouseEvent () { Flags = MouseFlags.Button1Released });
-			Assert.Null (Toplevel._dragPosition);
-		}
+				Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+					X = 0,
+					Y = 0,
+					Flags = MouseFlags.Button1Pressed
+				}));
 
-		#endregion
+				Assert.Null (Application.MouseGrabView);
 
+				Application.RequestStop ();
+			} else if (iterations == 2) {
+				Assert.Null (Application.MouseGrabView);
 
-		#region mousegrabtests
-		[Fact, AutoInitShutdown]
-		public void MouseGrabView_WithNullMouseEventView ()
-		{
-			var tf = new TextField () { Width = 10 };
-			var sv = new ScrollView () {
-				Width = Dim.Fill (),
-				Height = Dim.Fill (),
-				ContentSize = new Size (100, 100)
-			};
-
-			sv.Add (tf);
-			Application.Top.Add (sv);
-
-			var iterations = -1;
-
-			Application.Iteration = () => {
-				iterations++;
-				if (iterations == 0) {
-					Assert.True (tf.HasFocus);
-					Assert.Null (Application.MouseGrabView);
-
-					ReflectionTools.InvokePrivate (
-						typeof (Application),
-						"ProcessMouseEvent",
-						new MouseEvent () {
-							X = 5,
-							Y = 5,
-							Flags = MouseFlags.ReportMousePosition
-						});
-
-					Assert.Equal (sv, Application.MouseGrabView);
-
-					MessageBox.Query ("Title", "Test", "Ok");
-
-					Assert.Null (Application.MouseGrabView);
-				} else if (iterations == 1) {
-					// Application.MouseGrabView is null because
-					// another toplevel (Dialog) was opened
-					Assert.Null (Application.MouseGrabView);
-
-					ReflectionTools.InvokePrivate (
-						typeof (Application),
-						"ProcessMouseEvent",
-						new MouseEvent () {
-							X = 5,
-							Y = 5,
-							Flags = MouseFlags.ReportMousePosition
-						});
-
-					Assert.Null (Application.MouseGrabView);
-
-					ReflectionTools.InvokePrivate (
-						typeof (Application),
-						"ProcessMouseEvent",
-						new MouseEvent () {
-							X = 40,
-							Y = 12,
-							Flags = MouseFlags.ReportMousePosition
-						});
-
-					Assert.Null (Application.MouseGrabView);
-
-					ReflectionTools.InvokePrivate (
-						typeof (Application),
-						"ProcessMouseEvent",
-						new MouseEvent () {
-							X = 0,
-							Y = 0,
-							Flags = MouseFlags.Button1Pressed
-						});
-
-					Assert.Null (Application.MouseGrabView);
-
-					Application.RequestStop ();
-				} else if (iterations == 2) {
-					Assert.Null (Application.MouseGrabView);
-
-					Application.RequestStop ();
-				}
-			};
-
-			Application.Run ();
+				Application.RequestStop ();
+			}
+		};
+
+		Application.Run ();
+	}
+
+	[Fact, AutoInitShutdown]
+	public void MouseGrabView_GrabbedMouse_UnGrabbedMouse ()
+	{
+		View grabView = null;
+		var count = 0;
+
+		var view1 = new View ();
+		var view2 = new View ();
+
+		Application.GrabbedMouse += Application_GrabbedMouse;
+		Application.UnGrabbedMouse += Application_UnGrabbedMouse;
+
+		Application.GrabMouse (view1);
+		Assert.Equal (0, count);
+		Assert.Equal (grabView, view1);
+		Assert.Equal (view1, Application.MouseGrabView);
+
+		Application.UngrabMouse ();
+		Assert.Equal (1, count);
+		Assert.Equal (grabView, view1);
+		Assert.Null (Application.MouseGrabView);
+
+		Application.GrabbedMouse += Application_GrabbedMouse;
+		Application.UnGrabbedMouse += Application_UnGrabbedMouse;
+
+		Application.GrabMouse (view2);
+		Assert.Equal (1, count);
+		Assert.Equal (grabView, view2);
+		Assert.Equal (view2, Application.MouseGrabView);
+
+		Application.UngrabMouse ();
+		Assert.Equal (2, count);
+		Assert.Equal (grabView, view2);
+		Assert.Null (Application.MouseGrabView);
+
+		void Application_GrabbedMouse (object sender, ViewEventArgs e)
+		{
+			if (count == 0) {
+				Assert.Equal (view1, e.View);
+				grabView = view1;
+			} else {
+				Assert.Equal (view2, e.View);
+				grabView = view2;
+			}
+
+			Application.GrabbedMouse -= Application_GrabbedMouse;
 		}
 
-		[Fact, AutoInitShutdown]
-		public void MouseGrabView_GrabbedMouse_UnGrabbedMouse ()
+		void Application_UnGrabbedMouse (object sender, ViewEventArgs e)
 		{
-			View grabView = null;
-			var count = 0;
-
-			var view1 = new View ();
-			var view2 = new View ();
-
-			Application.GrabbedMouse += Application_GrabbedMouse;
-			Application.UnGrabbedMouse += Application_UnGrabbedMouse;
-
-			Application.GrabMouse (view1);
-			Assert.Equal (0, count);
-			Assert.Equal (grabView, view1);
-			Assert.Equal (view1, Application.MouseGrabView);
-
-			Application.UngrabMouse ();
-			Assert.Equal (1, count);
-			Assert.Equal (grabView, view1);
-			Assert.Null (Application.MouseGrabView);
-
-			Application.GrabbedMouse += Application_GrabbedMouse;
-			Application.UnGrabbedMouse += Application_UnGrabbedMouse;
-
-			Application.GrabMouse (view2);
-			Assert.Equal (1, count);
-			Assert.Equal (grabView, view2);
-			Assert.Equal (view2, Application.MouseGrabView);
-
-			Application.UngrabMouse ();
-			Assert.Equal (2, count);
-			Assert.Equal (grabView, view2);
-			Assert.Null (Application.MouseGrabView);
-
-			void Application_GrabbedMouse (object sender, ViewEventArgs e)
-			{
-				if (count == 0) {
-					Assert.Equal (view1, e.View);
-					grabView = view1;
-				} else {
-					Assert.Equal (view2, e.View);
-					grabView = view2;
-				}
-
-				Application.GrabbedMouse -= Application_GrabbedMouse;
+			if (count == 0) {
+				Assert.Equal (view1, e.View);
+				Assert.Equal (grabView, e.View);
+			} else {
+				Assert.Equal (view2, e.View);
+				Assert.Equal (grabView, e.View);
 			}
+			count++;
 
-			void Application_UnGrabbedMouse (object sender, ViewEventArgs e)
-			{
-				if (count == 0) {
-					Assert.Equal (view1, e.View);
-					Assert.Equal (grabView, e.View);
-				} else {
-					Assert.Equal (view2, e.View);
-					Assert.Equal (grabView, e.View);
-				}
-				count++;
-
-				Application.UnGrabbedMouse -= Application_UnGrabbedMouse;
-			}
+			Application.UnGrabbedMouse -= Application_UnGrabbedMouse;
 		}
-		#endregion
 	}
-}
+	#endregion
+}

+ 698 - 718
UnitTests/Application/MainLoopTests.cs

@@ -13,828 +13,808 @@ using Xunit.Sdk;
 // Alias Console to MockConsole so we don't accidentally use Console
 using Console = Terminal.Gui.FakeConsole;
 
-namespace Terminal.Gui.ApplicationTests {
-	/// <summary>
-	/// Tests MainLoop using the FakeMainLoop.
-	/// </summary>
-	public class MainLoopTests {
-
-		// TODO: Expand to test all the MainLoop implementations.
-
-		[Fact]
-		public void Constructor_Setups_Driver ()
-		{
-			var ml = new MainLoop (new FakeMainLoop ());
-			Assert.NotNull (ml.MainLoopDriver);
-		}
-
-		// Idle Handler tests
-		[Fact]
-		public void AddIdle_Adds_And_Removes ()
-		{
-			var ml = new MainLoop (new FakeMainLoop ());
-
-			Func<bool> fnTrue = () => true;
-			Func<bool> fnFalse = () => false;
-
-			ml.AddIdle (fnTrue);
-			ml.AddIdle (fnFalse);
-
-			Assert.Equal (2, ml.IdleHandlers.Count);
-			Assert.Equal (fnTrue, ml.IdleHandlers [0]);
-			Assert.NotEqual (fnFalse, ml.IdleHandlers [0]);
-
-			Assert.True (ml.RemoveIdle (fnTrue));
-			Assert.Single (ml.IdleHandlers);
-
-			// 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.RemoveIdle (fnTrue));
-
-			Assert.True (ml.RemoveIdle (fnFalse));
-
-			// 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.RemoveIdle (fnFalse));
-
-			// Add again, but with dupe
-			ml.AddIdle (fnTrue);
-			ml.AddIdle (fnTrue);
-
-			Assert.Equal (2, ml.IdleHandlers.Count);
-			Assert.Equal (fnTrue, ml.IdleHandlers [0]);
-			Assert.True (ml.IdleHandlers [0] ());
-			Assert.Equal (fnTrue, ml.IdleHandlers [1]);
-			Assert.True (ml.IdleHandlers [1] ());
-
-			Assert.True (ml.RemoveIdle (fnTrue));
-			Assert.Single (ml.IdleHandlers);
-			Assert.Equal (fnTrue, ml.IdleHandlers [0]);
-			Assert.NotEqual (fnFalse, ml.IdleHandlers [0]);
-
-			Assert.True (ml.RemoveIdle (fnTrue));
-			Assert.Empty (ml.IdleHandlers);
-
-			// 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.RemoveIdle (fnTrue));
-		}
-
-		[Fact]
-		public void AddIdle_Function_GetsCalled_OnIteration ()
-		{
-			var ml = new MainLoop (new FakeMainLoop ());
-
-			var functionCalled = 0;
-			Func<bool> fn = () => {
-				functionCalled++;
-				return true;
-			};
-
-			ml.AddIdle (fn);
-			ml.RunIteration ();
-			Assert.Equal (1, functionCalled);
-		}
-
-		[Fact]
-		public void RemoveIdle_Function_NotCalled ()
-		{
-			var ml = new MainLoop (new FakeMainLoop ());
-
-			var functionCalled = 0;
-			Func<bool> fn = () => {
-				functionCalled++;
-				return true;
-			};
-
-			Assert.False (ml.RemoveIdle (fn));
-			ml.RunIteration ();
-			Assert.Equal (0, functionCalled);
-		}
-
-		[Fact]
-		public void AddThenRemoveIdle_Function_NotCalled ()
-		{
-			var ml = new MainLoop (new FakeMainLoop ());
-
-			var functionCalled = 0;
-			Func<bool> fn = () => {
-				functionCalled++;
-				return true;
-			};
+namespace Terminal.Gui.ApplicationTests;
+/// <summary>
+/// Tests MainLoop using the FakeMainLoop.
+/// </summary>
+public class MainLoopTests {
+
+	// See Also ConsoleDRivers/MainLoopDriverTests.cs for tests of the MainLoopDriver
+	
+	
+	// Idle Handler tests
+	[Fact]
+	public void AddIdle_Adds_And_Removes ()
+	{
+		var ml = new MainLoop (new FakeMainLoop ());
+
+		Func<bool> fnTrue = () => true;
+		Func<bool> fnFalse = () => false;
+
+		ml.AddIdle (fnTrue);
+		ml.AddIdle (fnFalse);
+
+		Assert.Equal (2, ml.IdleHandlers.Count);
+		Assert.Equal (fnTrue, ml.IdleHandlers [0]);
+		Assert.NotEqual (fnFalse, ml.IdleHandlers [0]);
+
+		Assert.True (ml.RemoveIdle (fnTrue));
+		Assert.Single (ml.IdleHandlers);
+
+		// 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.RemoveIdle (fnTrue));
+
+		Assert.True (ml.RemoveIdle (fnFalse));
+
+		// 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.RemoveIdle (fnFalse));
+
+		// Add again, but with dupe
+		ml.AddIdle (fnTrue);
+		ml.AddIdle (fnTrue);
+
+		Assert.Equal (2, ml.IdleHandlers.Count);
+		Assert.Equal (fnTrue, ml.IdleHandlers [0]);
+		Assert.True (ml.IdleHandlers [0] ());
+		Assert.Equal (fnTrue, ml.IdleHandlers [1]);
+		Assert.True (ml.IdleHandlers [1] ());
+
+		Assert.True (ml.RemoveIdle (fnTrue));
+		Assert.Single (ml.IdleHandlers);
+		Assert.Equal (fnTrue, ml.IdleHandlers [0]);
+		Assert.NotEqual (fnFalse, ml.IdleHandlers [0]);
+
+		Assert.True (ml.RemoveIdle (fnTrue));
+		Assert.Empty (ml.IdleHandlers);
+
+		// 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.RemoveIdle (fnTrue));
+	}
 
-			ml.AddIdle (fn);
-			Assert.True (ml.RemoveIdle (fn));
-			ml.RunIteration ();
-			Assert.Equal (0, functionCalled);
-		}
+	[Fact]
+	public void AddIdle_Function_GetsCalled_OnIteration ()
+	{
+		var ml = new MainLoop (new FakeMainLoop ());
 
-		[Fact]
-		public void AddIdleTwice_Function_CalledTwice ()
-		{
-			var ml = new MainLoop (new FakeMainLoop ());
+		var functionCalled = 0;
+		Func<bool> fn = () => {
+			functionCalled++;
+			return true;
+		};
 
-			var functionCalled = 0;
-			Func<bool> fn = () => {
-				functionCalled++;
-				return true;
-			};
+		ml.AddIdle (fn);
+		ml.RunIteration ();
+		Assert.Equal (1, functionCalled);
+	}
 
-			ml.AddIdle (fn);
-			ml.AddIdle (fn);
-			ml.RunIteration ();
-			Assert.Equal (2, functionCalled);
-			Assert.Equal (2, ml.IdleHandlers.Count);
-
-			functionCalled = 0;
-			Assert.True (ml.RemoveIdle (fn));
-			Assert.Single (ml.IdleHandlers);
-			ml.RunIteration ();
-			Assert.Equal (1, functionCalled);
-
-			functionCalled = 0;
-			Assert.True (ml.RemoveIdle (fn));
-			Assert.Empty (ml.IdleHandlers);
-			ml.RunIteration ();
-			Assert.Equal (0, functionCalled);
-			Assert.False (ml.RemoveIdle (fn));
-		}
+	[Fact]
+	public void RemoveIdle_Function_NotCalled ()
+	{
+		var ml = new MainLoop (new FakeMainLoop ());
 
-		[Fact]
-		public void False_Idle_Stops_It_Being_Called_Again ()
-		{
-			var ml = new MainLoop (new FakeMainLoop ());
+		var functionCalled = 0;
+		Func<bool> fn = () => {
+			functionCalled++;
+			return true;
+		};
 
-			var functionCalled = 0;
-			Func<bool> fn1 = () => {
-				functionCalled++;
-				if (functionCalled == 10) return false;
-				return true;
-			};
+		Assert.False (ml.RemoveIdle (fn));
+		ml.RunIteration ();
+		Assert.Equal (0, functionCalled);
+	}
 
-			// Force stop if 20 iterations
-			var stopCount = 0;
-			Func<bool> fnStop = () => {
-				stopCount++;
-				if (stopCount == 20) ml.Stop ();
-				return true;
-			};
+	[Fact]
+	public void AddThenRemoveIdle_Function_NotCalled ()
+	{
+		var ml = new MainLoop (new FakeMainLoop ());
+
+		var functionCalled = 0;
+		Func<bool> fn = () => {
+			functionCalled++;
+			return true;
+		};
+
+		ml.AddIdle (fn);
+		Assert.True (ml.RemoveIdle (fn));
+		ml.RunIteration ();
+		Assert.Equal (0, functionCalled);
+	}
 
-			ml.AddIdle (fnStop);
-			ml.AddIdle (fn1);
-			ml.Run ();
-			Assert.True (ml.RemoveIdle (fnStop));
-			Assert.False (ml.RemoveIdle (fn1));
+	[Fact]
+	public void AddIdleTwice_Function_CalledTwice ()
+	{
+		var ml = new MainLoop (new FakeMainLoop ());
+
+		var functionCalled = 0;
+		Func<bool> fn = () => {
+			functionCalled++;
+			return true;
+		};
+
+		ml.AddIdle (fn);
+		ml.AddIdle (fn);
+		ml.RunIteration ();
+		Assert.Equal (2, functionCalled);
+		Assert.Equal (2, ml.IdleHandlers.Count);
+
+		functionCalled = 0;
+		Assert.True (ml.RemoveIdle (fn));
+		Assert.Single (ml.IdleHandlers);
+		ml.RunIteration ();
+		Assert.Equal (1, functionCalled);
+
+		functionCalled = 0;
+		Assert.True (ml.RemoveIdle (fn));
+		Assert.Empty (ml.IdleHandlers);
+		ml.RunIteration ();
+		Assert.Equal (0, functionCalled);
+		Assert.False (ml.RemoveIdle (fn));
+	}
 
-			Assert.Equal (10, functionCalled);
-			Assert.Equal (20, stopCount);
-		}
+	[Fact]
+	public void False_Idle_Stops_It_Being_Called_Again ()
+	{
+		var ml = new MainLoop (new FakeMainLoop ());
+
+		var functionCalled = 0;
+		Func<bool> fn1 = () => {
+			functionCalled++;
+			if (functionCalled == 10) return false;
+			return true;
+		};
+
+		// Force stop if 20 iterations
+		var stopCount = 0;
+		Func<bool> fnStop = () => {
+			stopCount++;
+			if (stopCount == 20) ml.Stop ();
+			return true;
+		};
+
+		ml.AddIdle (fnStop);
+		ml.AddIdle (fn1);
+		ml.Run ();
+		Assert.True (ml.RemoveIdle (fnStop));
+		Assert.False (ml.RemoveIdle (fn1));
+
+		Assert.Equal (10, functionCalled);
+		Assert.Equal (20, stopCount);
+	}
 
-		[Fact]
-		public void AddIdle_Twice_Returns_False_Called_Twice ()
-		{
-			var ml = new MainLoop (new FakeMainLoop ());
+	[Fact]
+	public void AddIdle_Twice_Returns_False_Called_Twice ()
+	{
+		var ml = new MainLoop (new FakeMainLoop ());
+
+		var functionCalled = 0;
+		Func<bool> fn1 = () => {
+			functionCalled++;
+			return false;
+		};
+
+		// Force stop if 10 iterations
+		var stopCount = 0;
+		Func<bool> fnStop = () => {
+			stopCount++;
+			if (stopCount == 10) ml.Stop ();
+			return true;
+		};
+
+		ml.AddIdle (fnStop);
+		ml.AddIdle (fn1);
+		ml.AddIdle (fn1);
+		ml.Run ();
+		Assert.True (ml.RemoveIdle (fnStop));
+		Assert.False (ml.RemoveIdle (fn1));
+		Assert.False (ml.RemoveIdle (fn1));
+
+		Assert.Equal (2, functionCalled);
+	}
 
-			var functionCalled = 0;
-			Func<bool> fn1 = () => {
-				functionCalled++;
-				return false;
-			};
+	[Fact]
+	public void Run_Runs_Idle_Stop_Stops_Idle ()
+	{
+		var ml = new MainLoop (new FakeMainLoop ());
 
-			// Force stop if 10 iterations
-			var stopCount = 0;
-			Func<bool> fnStop = () => {
-				stopCount++;
-				if (stopCount == 10) ml.Stop ();
-				return true;
-			};
+		var functionCalled = 0;
+		Func<bool> fn = () => {
+			functionCalled++;
+			if (functionCalled == 10) {
+				ml.Stop ();
+			}
+			return true;
+		};
 
-			ml.AddIdle (fnStop);
-			ml.AddIdle (fn1);
-			ml.AddIdle (fn1);
-			ml.Run ();
-			Assert.True (ml.RemoveIdle (fnStop));
-			Assert.False (ml.RemoveIdle (fn1));
-			Assert.False (ml.RemoveIdle (fn1));
+		ml.AddIdle (fn);
+		ml.Run ();
+		Assert.True (ml.RemoveIdle (fn));
 
-			Assert.Equal (2, functionCalled);
-		}
+		Assert.Equal (10, functionCalled);
+	}
 
-		[Fact]
-		public void Run_Runs_Idle_Stop_Stops_Idle ()
-		{
-			var ml = new MainLoop (new FakeMainLoop ());
+	// Timeout Handler Tests
+	[Fact]
+	public void AddTimer_Adds_Removes_NoFaults ()
+	{
+		var ml = new MainLoop (new FakeMainLoop ());
+		var ms = 100;
 
-			var functionCalled = 0;
-			Func<bool> fn = () => {
-				functionCalled++;
-				if (functionCalled == 10) {
-					ml.Stop ();
-				}
-				return true;
-			};
+		var callbackCount = 0;
+		Func<bool> callback = () => {
+			callbackCount++;
+			return true;
+		};
 
-			ml.AddIdle (fn);
-			ml.Run ();
-			Assert.True (ml.RemoveIdle (fn));
+		var token = ml.AddTimeout (TimeSpan.FromMilliseconds (ms), callback);
 
-			Assert.Equal (10, functionCalled);
-		}
+		Assert.True (ml.RemoveTimeout (token));
 
-		// Timeout Handler Tests
-		[Fact]
-		public void AddTimer_Adds_Removes_NoFaults ()
-		{
-			var ml = new MainLoop (new FakeMainLoop ());
-			var ms = 100;
+		// BUGBUG: This should probably fault?
+		// Must return a boolean.
+		Assert.False (ml.RemoveTimeout (token));
+	}
 
-			var callbackCount = 0;
-			Func<MainLoop, bool> callback = (loop) => {
-				callbackCount++;
-				return true;
-			};
+	// Timeout Handler Tests
+	[Fact]
+	public void AddTimer_EventFired ()
+	{
+		var ml = new MainLoop (new FakeMainLoop ());
+		var ms = 100;
 
-			var token = ml.AddTimeout (TimeSpan.FromMilliseconds (ms), callback);
+		var originTicks = DateTime.UtcNow.Ticks;
 
-			Assert.True (ml.RemoveTimeout (token));
+		var callbackCount = 0;
+		Func<bool> callback = () => {
+			callbackCount++;
+			return true;
+		};
 
-			// BUGBUG: This should probably fault?
-			// Must return a boolean.
-			Assert.False (ml.RemoveTimeout (token));
-		}
+		object sender = null;
+		TimeoutEventArgs args = null;
+		ml.TimeoutAdded += (s, e) => {
+			sender = s;
+			args = e;
+		};
 
-		// Timeout Handler Tests
-		[Fact]
-		public void AddTimer_EventFired ()
-		{
-			var ml = new MainLoop (new FakeMainLoop ());
-			var ms = 100;
+		var token = ml.AddTimeout (TimeSpan.FromMilliseconds (ms), callback);
 
-			var originTicks = DateTime.UtcNow.Ticks;
+		Assert.Same (ml, sender);
+		Assert.NotNull (args.Timeout);
+		Assert.True (args.Ticks - originTicks >= 100 * TimeSpan.TicksPerMillisecond);
 
-			var callbackCount = 0;
-			Func<MainLoop, bool> callback = (loop) => {
-				callbackCount++;
-				return true;
-			};
+	}
+	[Fact]
+	public void AddTimer_Run_Called ()
+	{
+		var ml = new MainLoop (new FakeMainLoop ());
+		var ms = 100;
+
+		var callbackCount = 0;
+		Func<bool> callback = () => {
+			callbackCount++;
+			ml.Stop ();
+			return true;
+		};
+
+		var token = ml.AddTimeout (TimeSpan.FromMilliseconds (ms), callback);
+		ml.Run ();
+		Assert.True (ml.RemoveTimeout (token));
+
+		Assert.Equal (1, callbackCount);
+	}
 
-			object sender = null;
-			TimeoutEventArgs args = null;
-			ml.TimeoutAdded += (s, e) => {
-				sender = s;
-				args = e;
-			};
+	[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<bool> callback = () => {
+			callbackCount++;
+			if (callbackCount == 2) ml.Stop ();
+			return true;
+		};
+
+		var task1 = new Task (() => token1 = ml.AddTimeout (TimeSpan.FromMilliseconds (ms), callback));
+		var task2 = new Task (() => token2 = ml.AddTimeout (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.RemoveTimeout (token1));
+		Assert.True (ml.RemoveTimeout (token2));
+
+		Assert.Equal (2, callbackCount);
+	}
 
-			var token = ml.AddTimeout (TimeSpan.FromMilliseconds (ms), callback);
+	[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<bool> callback = () => {
+			callbackCount++;
+			if (callbackCount == 2) ml.Stop ();
+			return true;
+		};
+
+		Parallel.Invoke (
+			() => token1 = ml.AddTimeout (TimeSpan.FromMilliseconds (ms), callback),
+			() => token2 = ml.AddTimeout (TimeSpan.FromMilliseconds (ms), callback)
+		);
+		ml.Run ();
+		Assert.NotNull (token1);
+		Assert.NotNull (token2);
+		Assert.True (ml.RemoveTimeout (token1));
+		Assert.True (ml.RemoveTimeout (token2));
+
+		Assert.Equal (2, callbackCount);
+	}
 
-			Assert.Same (ml, sender);
-			Assert.NotNull (args.Timeout);
-			Assert.True (args.Ticks - originTicks >= 100 * TimeSpan.TicksPerMillisecond);
+	class MillisecondTolerance : IEqualityComparer<TimeSpan> {
+		int _tolerance = 0;
+		public MillisecondTolerance (int tolerance) { _tolerance = tolerance; }
+		public bool Equals (TimeSpan x, TimeSpan y) => Math.Abs (x.Milliseconds - y.Milliseconds) <= _tolerance;
+		public int GetHashCode (TimeSpan obj) => obj.GetHashCode ();
+	}
 
-		}
-		[Fact]
-		public void AddTimer_Run_Called ()
-		{
-			var ml = new MainLoop (new FakeMainLoop ());
-			var ms = 100;
+	[Fact]
+	public void AddTimer_Run_CalledAtApproximatelyRightTime ()
+	{
+		var ml = new MainLoop (new FakeMainLoop ());
+		var ms = TimeSpan.FromMilliseconds (50);
+		var watch = new System.Diagnostics.Stopwatch ();
+
+		var callbackCount = 0;
+		Func<bool> callback = () => {
+			watch.Stop ();
+			callbackCount++;
+			ml.Stop ();
+			return true;
+		};
+
+		var token = ml.AddTimeout (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.RemoveTimeout (token));
+		Assert.Equal (1, callbackCount);
+	}
 
-			var callbackCount = 0;
-			Func<MainLoop, bool> callback = (loop) => {
-				callbackCount++;
+	[Fact]
+	public void AddTimer_Run_CalledTwiceApproximatelyRightTime ()
+	{
+		var ml = new MainLoop (new FakeMainLoop ());
+		var ms = TimeSpan.FromMilliseconds (50);
+		var watch = new System.Diagnostics.Stopwatch ();
+
+		var callbackCount = 0;
+		Func<bool> callback = () => {
+			callbackCount++;
+			if (callbackCount == 2) {
+				watch.Stop ();
 				ml.Stop ();
-				return true;
-			};
-
-			var token = ml.AddTimeout (TimeSpan.FromMilliseconds (ms), callback);
-			ml.Run ();
-			Assert.True (ml.RemoveTimeout (token));
-
-			Assert.Equal (1, callbackCount);
-		}
-
-		[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<MainLoop, bool> callback = (loop) => {
-				callbackCount++;
-				if (callbackCount == 2) ml.Stop ();
-				return true;
-			};
-
-			var task1 = new Task (() => token1 = ml.AddTimeout (TimeSpan.FromMilliseconds (ms), callback));
-			var task2 = new Task (() => token2 = ml.AddTimeout (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.RemoveTimeout (token1));
-			Assert.True (ml.RemoveTimeout (token2));
-
-			Assert.Equal (2, callbackCount);
-		}
-
-		[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<MainLoop, bool> callback = (loop) => {
-				callbackCount++;
-				if (callbackCount == 2) ml.Stop ();
-				return true;
-			};
+			}
+			return true;
+		};
+
+		var token = ml.AddTimeout (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.RemoveTimeout (token));
+		Assert.Equal (2, callbackCount);
+	}
 
-			Parallel.Invoke (
-				() => token1 = ml.AddTimeout (TimeSpan.FromMilliseconds (ms), callback),
-				() => token2 = ml.AddTimeout (TimeSpan.FromMilliseconds (ms), callback)
-			);
-			ml.Run ();
-			Assert.NotNull (token1);
-			Assert.NotNull (token2);
-			Assert.True (ml.RemoveTimeout (token1));
-			Assert.True (ml.RemoveTimeout (token2));
-
-			Assert.Equal (2, callbackCount);
-		}
+	[Fact]
+	public void AddTimer_Remove_NotCalled ()
+	{
+		var ml = new MainLoop (new FakeMainLoop ());
+		var ms = TimeSpan.FromMilliseconds (50);
+
+		// Force stop if 10 iterations
+		var stopCount = 0;
+		Func<bool> fnStop = () => {
+			stopCount++;
+			if (stopCount == 10) ml.Stop ();
+			return true;
+		};
+		ml.AddIdle (fnStop);
+
+		var callbackCount = 0;
+		Func<bool> callback = () => {
+			callbackCount++;
+			return true;
+		};
+
+		var token = ml.AddTimeout (ms, callback);
+		Assert.True (ml.RemoveTimeout (token));
+		ml.Run ();
+		Assert.Equal (0, callbackCount);
+	}
 
-		class MillisecondTolerance : IEqualityComparer<TimeSpan> {
-			int _tolerance = 0;
-			public MillisecondTolerance (int tolerance) { _tolerance = tolerance; }
-			public bool Equals (TimeSpan x, TimeSpan y) => Math.Abs (x.Milliseconds - y.Milliseconds) <= _tolerance;
-			public int GetHashCode (TimeSpan obj) => obj.GetHashCode ();
-		}
+	[Fact]
+	public void AddTimer_ReturnFalse_StopsBeingCalled ()
+	{
+		var ml = new MainLoop (new FakeMainLoop ());
+		var ms = TimeSpan.FromMilliseconds (50);
+
+		// Force stop if 10 iterations
+		var stopCount = 0;
+		Func<bool> fnStop = () => {
+			Thread.Sleep (10); // Sleep to enable timer to fire
+			stopCount++;
+			if (stopCount == 10) ml.Stop ();
+			return true;
+		};
+		ml.AddIdle (fnStop);
+
+		var callbackCount = 0;
+		Func<bool> callback = () => {
+			callbackCount++;
+			return false;
+		};
+
+		var token = ml.AddTimeout (ms, callback);
+		ml.Run ();
+		Assert.Equal (1, callbackCount);
+		Assert.Equal (10, stopCount);
+		Assert.False (ml.RemoveTimeout (token));
+	}
 
-		[Fact]
-		public void AddTimer_Run_CalledAtApproximatelyRightTime ()
-		{
-			var ml = new MainLoop (new FakeMainLoop ());
-			var ms = TimeSpan.FromMilliseconds (50);
-			var watch = new System.Diagnostics.Stopwatch ();
+	[Fact]
+	public void CheckTimersAndIdleHandlers_NoTimers_Returns_False ()
+	{
+		var ml = new MainLoop (new FakeMainLoop ());
+		var retVal = ml.CheckTimersAndIdleHandlers (out var waitTimeOut);
+		Assert.False (retVal);
+		Assert.Equal (-1, waitTimeOut);
+	}
 
-			var callbackCount = 0;
-			Func<MainLoop, bool> callback = (loop) => {
-				watch.Stop ();
-				callbackCount++;
-				ml.Stop ();
-				return true;
-			};
+	[Fact]
+	public void CheckTimersAndIdleHandlers_NoTimers_WithIdle_Returns_True ()
+	{
+		var ml = new MainLoop (new FakeMainLoop ());
+		Func<bool> fnTrue = () => true;
 
-			var token = ml.AddTimeout (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));
+		ml.AddIdle (fnTrue);
+		var retVal = ml.CheckTimersAndIdleHandlers (out var waitTimeOut);
+		Assert.True (retVal);
+		Assert.Equal (-1, waitTimeOut);
+	}
 
-			Assert.True (ml.RemoveTimeout (token));
-			Assert.Equal (1, callbackCount);
-		}
+	[Fact]
+	public void CheckTimersAndIdleHandlers_With1Timer_Returns_Timer ()
+	{
+		var ml = new MainLoop (new FakeMainLoop ());
+		var ms = TimeSpan.FromMilliseconds (50);
 
-		[Fact]
-		public void AddTimer_Run_CalledTwiceApproximatelyRightTime ()
-		{
-			var ml = new MainLoop (new FakeMainLoop ());
-			var ms = TimeSpan.FromMilliseconds (50);
-			var watch = new System.Diagnostics.Stopwatch ();
-
-			var callbackCount = 0;
-			Func<MainLoop, bool> callback = (loop) => {
-				callbackCount++;
-				if (callbackCount == 2) {
-					watch.Stop ();
-					ml.Stop ();
-				}
-				return true;
-			};
+		static bool Callback () => false;
 
-			var token = ml.AddTimeout (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));
+		_ = ml.AddTimeout (ms, Callback);
+		var retVal = ml.CheckTimersAndIdleHandlers (out var waitTimeOut);
 
-			Assert.True (ml.RemoveTimeout (token));
-			Assert.Equal (2, callbackCount);
-		}
+		Assert.True (retVal);
+		// It should take < 10ms to execute to here
+		Assert.True (ms.TotalMilliseconds <= (waitTimeOut + 10));
+	}
 
-		[Fact]
-		public void AddTimer_Remove_NotCalled ()
-		{
-			var ml = new MainLoop (new FakeMainLoop ());
-			var ms = TimeSpan.FromMilliseconds (50);
-
-			// Force stop if 10 iterations
-			var stopCount = 0;
-			Func<bool> fnStop = () => {
-				stopCount++;
-				if (stopCount == 10) ml.Stop ();
-				return true;
-			};
-			ml.AddIdle (fnStop);
+	[Fact]
+	public void CheckTimersAndIdleHandlers_With2Timers_Returns_Timer ()
+	{
+		var ml = new MainLoop (new FakeMainLoop ());
+		var ms = TimeSpan.FromMilliseconds (50);
 
-			var callbackCount = 0;
-			Func<MainLoop, bool> callback = (loop) => {
-				callbackCount++;
-				return true;
-			};
+		static bool Callback () => false;
 
-			var token = ml.AddTimeout (ms, callback);
-			Assert.True (ml.RemoveTimeout (token));
-			ml.Run ();
-			Assert.Equal (0, callbackCount);
-		}
+		_ = ml.AddTimeout (ms, Callback);
+		_ = ml.AddTimeout (ms, Callback);
+		var retVal = ml.CheckTimersAndIdleHandlers (out var waitTimeOut);
 
-		[Fact]
-		public void AddTimer_ReturnFalse_StopsBeingCalled ()
-		{
-			var ml = new MainLoop (new FakeMainLoop ());
-			var ms = TimeSpan.FromMilliseconds (50);
-
-			// Force stop if 10 iterations
-			var stopCount = 0;
-			Func<bool> fnStop = () => {
-				Thread.Sleep (10); // Sleep to enable timer to fire
-				stopCount++;
-				if (stopCount == 10) ml.Stop ();
-				return true;
-			};
-			ml.AddIdle (fnStop);
+		Assert.True (retVal);
+		// It should take < 10ms to execute to here
+		Assert.True (ms.TotalMilliseconds <= (waitTimeOut + 10));
+	}
 
-			var callbackCount = 0;
-			Func<MainLoop, bool> callback = (loop) => {
-				callbackCount++;
-				return false;
-			};
+	[Fact]
+	public void Internal_Tests ()
+	{
+		var testMainloop = new TestMainloop ();
+		var mainloop = new MainLoop (testMainloop);
+		Assert.Empty (mainloop._timeouts);
+		Assert.Empty (mainloop._idleHandlers);
+		Assert.NotNull (new Timeout () {
+			Span = new TimeSpan (),
+			Callback = () => true
+		});
+	}
 
-			var token = ml.AddTimeout (ms, callback);
-			ml.Run ();
-			Assert.Equal (1, callbackCount);
-			Assert.Equal (10, stopCount);
-			Assert.False (ml.RemoveTimeout (token));
-		}
+	private class TestMainloop : IMainLoopDriver {
+		private MainLoop mainLoop;
 
-		// Invoke Tests
-		// TODO: Test with threading scenarios
-		[Fact]
-		public void Invoke_Adds_Idle ()
+		public bool EventsPending ()
 		{
-			var ml = new MainLoop (new FakeMainLoop ());
-
-			var actionCalled = 0;
-			ml.Invoke (() => { actionCalled++; });
-			ml.RunIteration ();
-			Assert.Equal (1, actionCalled);
+			throw new NotImplementedException ();
 		}
 
-		[Fact]
-		public void CheckTimersAndIdleHandlers_NoTimers_Returns_False ()
+		public void Iteration ()
 		{
-			var ml = new MainLoop (new FakeMainLoop ());
-			var retVal = ml.CheckTimersAndIdleHandlers (out var waitTimeOut);
-			Assert.False (retVal);
-			Assert.Equal (-1, waitTimeOut);
+			throw new NotImplementedException ();
 		}
-
-		[Fact]
-		public void CheckTimersAndIdleHandlers_NoTimers_WithIdle_Returns_True ()
+		public void TearDown ()
 		{
-			var ml = new MainLoop (new FakeMainLoop ());
-			Func<bool> fnTrue = () => true;
-
-			ml.AddIdle (fnTrue);
-			var retVal = ml.CheckTimersAndIdleHandlers (out var waitTimeOut);
-			Assert.True (retVal);
-			Assert.Equal (-1, waitTimeOut);
+			throw new NotImplementedException ();
 		}
 
-		[Fact]
-		public void CheckTimersAndIdleHandlers_With1Timer_Returns_Timer ()
+		public void Setup (MainLoop mainLoop)
 		{
-			var ml = new MainLoop (new FakeMainLoop ());
-			var ms = TimeSpan.FromMilliseconds (50);
-
-			static bool Callback (MainLoop loop) => false;
-
-			_ = ml.AddTimeout (ms, Callback);
-			var retVal = ml.CheckTimersAndIdleHandlers (out var waitTimeOut);
-
-			Assert.True (retVal);
-			// It should take < 10ms to execute to here
-			Assert.True (ms.TotalMilliseconds <= (waitTimeOut + 10));
+			this.mainLoop = mainLoop;
 		}
 
-		[Fact]
-		public void CheckTimersAndIdleHandlers_With2Timers_Returns_Timer ()
+		public void Wakeup ()
 		{
-			var ml = new MainLoop (new FakeMainLoop ());
-			var ms = TimeSpan.FromMilliseconds (50);
-
-			static bool Callback (MainLoop loop) => false;
-
-			_ = ml.AddTimeout (ms, Callback);
-			_ = ml.AddTimeout (ms, Callback);
-			var retVal = ml.CheckTimersAndIdleHandlers (out var waitTimeOut);
-
-			Assert.True (retVal);
-			// It should take < 10ms to execute to here
-			Assert.True (ms.TotalMilliseconds <= (waitTimeOut + 10));
+			throw new NotImplementedException ();
 		}
+	}
 
-		[Fact]
-		public void Internal_Tests ()
-		{
-			var testMainloop = new TestMainloop ();
-			var mainloop = new MainLoop (testMainloop);
-			Assert.Empty (mainloop._timeouts);
-			Assert.Empty (mainloop._idleHandlers);
-			Assert.NotNull (new Timeout () {
-				Span = new TimeSpan (),
-				Callback = (_) => true
+	// TODO: EventsPending tests
+	// - wait = true
+	// - wait = false
+
+	// TODO: Add IMainLoop tests
+
+	volatile static int tbCounter = 0;
+	static ManualResetEventSlim _wakeUp = new ManualResetEventSlim (false);
+
+	private static void Launch (Random r, TextField tf, int target)
+	{
+		Task.Run (() => {
+			Thread.Sleep (r.Next (2, 4));
+			Application.Invoke (() => {
+				tf.Text = $"index{r.Next ()}";
+				Interlocked.Increment (ref tbCounter);
+				if (target == tbCounter) {
+					// On last increment wake up the check
+					_wakeUp.Set ();
+				}
 			});
-		}
-
-		private class TestMainloop : IMainLoopDriver {
-			private MainLoop mainLoop;
+		});
+	}
 
-			public bool EventsPending ()
-			{
-				throw new NotImplementedException ();
-			}
+	private static void RunTest (Random r, TextField tf, int numPasses, int numIncrements, int pollMs)
+	{
+		for (int j = 0; j < numPasses; j++) {
 
-			public void Iteration ()
-			{
-				throw new NotImplementedException ();
-			}
-			public void TearDown ()
-			{
-				throw new NotImplementedException ();
-			}
+			_wakeUp.Reset ();
+			for (var i = 0; i < numIncrements; i++) Launch (r, tf, (j + 1) * numIncrements);
 
-			public void Setup (MainLoop mainLoop)
+			while (tbCounter != (j + 1) * numIncrements) // Wait for tbCounter to reach expected value
 			{
-				this.mainLoop = mainLoop;
-			}
-
-			public void Wakeup ()
-			{
-				throw new NotImplementedException ();
-			}
+				var tbNow = tbCounter;
+				_wakeUp.Wait (pollMs);
+				if (tbCounter == tbNow) {
+					// No change after wait: Idle handlers added via Application.Invoke have gone missing
+					Application.Invoke (() => Application.RequestStop ());
+					throw new TimeoutException (
+						$"Timeout: Increment lost. tbCounter ({tbCounter}) didn't " +
+						$"change after waiting {pollMs} ms. Failed to reach {(j + 1) * numIncrements} on pass {j + 1}");
+				}
+			};
 		}
+		Application.Invoke (() => Application.RequestStop ());
+	}
 
-		// TODO: EventsPending tests
-		// - wait = true
-		// - wait = false
+	[Fact]
+	[AutoInitShutdown]
+	public async Task InvokeLeakTest ()
+	{
+		Random r = new ();
+		TextField tf = new ();
+		Application.Top.Add (tf);
 
-		// TODO: Add IMainLoop tests
+		const int numPasses = 5;
+		const int numIncrements = 5000;
+		const int pollMs = 10000;
 
-		volatile static int tbCounter = 0;
-		static ManualResetEventSlim _wakeUp = new ManualResetEventSlim (false);
+		var task = Task.Run (() => RunTest (r, tf, numPasses, numIncrements, pollMs));
 
-		private static void Launch (Random r, TextField tf, int target)
-		{
-			Task.Run (() => {
-				Thread.Sleep (r.Next (2, 4));
-				Application.MainLoop.Invoke (() => {
-					tf.Text = $"index{r.Next ()}";
-					Interlocked.Increment (ref tbCounter);
-					if (target == tbCounter) {
-						// On last increment wake up the check
-						_wakeUp.Set ();
-					}
-				});
-			});
-		}
+		// blocks here until the RequestStop is processed at the end of the test
+		Application.Run ();
 
-		private static void RunTest (Random r, TextField tf, int numPasses, int numIncrements, int pollMs)
-		{
-			for (int j = 0; j < numPasses; j++) {
-
-				_wakeUp.Reset ();
-				for (var i = 0; i < numIncrements; i++) Launch (r, tf, (j + 1) * numIncrements);
-
-				while (tbCounter != (j + 1) * numIncrements) // Wait for tbCounter to reach expected value
-				{
-					var tbNow = tbCounter;
-					_wakeUp.Wait (pollMs);
-					if (tbCounter == tbNow) {
-						// No change after wait: Idle handlers added via Application.MainLoop.Invoke have gone missing
-						Application.MainLoop.Invoke (() => Application.RequestStop ());
-						throw new TimeoutException (
-							$"Timeout: Increment lost. tbCounter ({tbCounter}) didn't " +
-							$"change after waiting {pollMs} ms. Failed to reach {(j + 1) * numIncrements} on pass {j + 1}");
-					}
-				};
-			}
-			Application.MainLoop.Invoke (() => Application.RequestStop ());
-		}
+		await task; // Propagate exception if any occurred
 
-		[Fact]
-		[AutoInitShutdown]
-		public async Task InvokeLeakTest ()
-		{
-			Random r = new ();
-			TextField tf = new ();
-			Application.Top.Add (tf);
+		Assert.Equal (numIncrements * numPasses, tbCounter);
+	}
 
-			const int numPasses = 5;
-			const int numIncrements = 5000;
-			const int pollMs = 10000;
+	private static int total;
+	private static Button btn;
+	private static string clickMe;
+	private static string cancel;
+	private static string pewPew;
+	private static int zero;
+	private static int one;
+	private static int two;
+	private static int three;
+	private static int four;
+	private static bool taskCompleted;
+
+	[Theory, AutoInitShutdown]
+	[MemberData (nameof (TestAddIdle))]
+	public void Mainloop_Invoke_Or_AddIdle_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)
+	{
+		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 ("Open Window");
+
+		btnLaunch.Clicked += (s, e) => action ();
+
+		Application.Top.Add (btnLaunch);
+
+		var iterations = -1;
+
+		Application.Iteration += (s, a) => {
+			iterations++;
+			if (iterations == 0) {
+				Assert.Null (btn);
+				Assert.Equal (zero, total);
+				Assert.True (btnLaunch.ProcessKey (new KeyEvent (Key.Enter, null)));
+				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.True (btn.ProcessKey (new KeyEvent (Key.Enter, null)));
+				Assert.Equal (cancel, btn.Text);
+				Assert.Equal (one, total);
+			} else if (taskCompleted) {
+				Application.RequestStop ();
+			}
+		};
 
-			var task = Task.Run (() => RunTest (r, tf, numPasses, numIncrements, pollMs));
+		Application.Run ();
 
-			// blocks here until the RequestStop is processed at the end of the test
-			Application.Run ();
+		Assert.True (taskCompleted);
+		Assert.Equal (clickMe, btn.Text);
+		Assert.Equal (four, total);
+	}
 
-			await task; // Propagate exception if any occurred
+	public static IEnumerable<object []> TestAddIdle {
+		get {
+			// Goes fine
+			Action a1 = StartWindow;
+			yield return new object [] { a1, "Click Me", "Cancel", "Pew Pew", 0, 1, 2, 3, 4 };
 
-			Assert.Equal (numIncrements * numPasses, tbCounter);
+			// Also goes fine
+			Action a2 = () => Application.Invoke (StartWindow);
+			yield return new object [] { a2, "Click Me", "Cancel", "Pew Pew", 0, 1, 2, 3, 4 };
 		}
+	}
 
-		private static int total;
-		private static Button btn;
-		private static string clickMe;
-		private static string cancel;
-		private static string pewPew;
-		private static int zero;
-		private static int one;
-		private static int two;
-		private static int three;
-		private static int four;
-		private static bool taskCompleted;
-
-		[Theory, AutoInitShutdown]
-		[MemberData (nameof (TestAddIdle))]
-		public void Mainloop_Invoke_Or_AddIdle_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)
-		{
-			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 ("Open Window");
-
-			btnLaunch.Clicked += (s, e) => action ();
+	private static void StartWindow ()
+	{
+		var startWindow = new Window {
+			Modal = true
+		};
 
-			Application.Top.Add (btnLaunch);
+		btn = new Button {
+			Text = "Click Me"
+		};
 
-			var iterations = -1;
+		btn.Clicked += RunAsyncTest;
 
-			Application.Iteration += () => {
-				iterations++;
-				if (iterations == 0) {
-					Assert.Null (btn);
-					Assert.Equal (zero, total);
-					Assert.True (btnLaunch.ProcessKey (new KeyEvent (Key.Enter, null)));
-					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.True (btn.ProcessKey (new KeyEvent (Key.Enter, null)));
-					Assert.Equal (cancel, btn.Text);
-					Assert.Equal (one, total);
-				} else if (taskCompleted) {
-					Application.RequestStop ();
-				}
-			};
+		var totalbtn = new Button () {
+			X = Pos.Right (btn),
+			Text = "total"
+		};
 
-			Application.Run ();
+		totalbtn.Clicked += (s, e) => {
+			MessageBox.Query ("Count", $"Count is {total}", "Ok");
+		};
 
-			Assert.True (taskCompleted);
-			Assert.Equal (clickMe, btn.Text);
-			Assert.Equal (four, total);
-		}
+		startWindow.Add (btn);
+		startWindow.Add (totalbtn);
 
-		public static IEnumerable<object []> TestAddIdle {
-			get {
-				// Goes fine
-				Action a1 = StartWindow;
-				yield return new object [] { a1, "Click Me", "Cancel", "Pew Pew", 0, 1, 2, 3, 4 };
+		Application.Run (startWindow);
 
-				// Also goes fine
-				Action a2 = () => Application.MainLoop.Invoke (StartWindow);
-				yield return new object [] { a2, "Click Me", "Cancel", "Pew Pew", 0, 1, 2, 3, 4 };
-			}
-		}
+		Assert.Equal (clickMe, btn.Text);
+		Assert.Equal (four, total);
 
-		private static void StartWindow ()
-		{
-			var startWindow = new Window {
-				Modal = true
-			};
+		Application.RequestStop ();
+	}
 
-			btn = new Button {
-				Text = "Click Me"
-			};
+	private static async void RunAsyncTest (object sender, EventArgs e)
+	{
+		Assert.Equal (clickMe, btn.Text);
+		Assert.Equal (zero, total);
 
-			btn.Clicked += RunAsyncTest;
+		btn.Text = "Cancel";
+		Interlocked.Increment (ref total);
+		btn.SetNeedsDisplay ();
 
-			var totalbtn = new Button () {
-				X = Pos.Right (btn),
-				Text = "total"
-			};
+		await Task.Run (() => {
+			try {
+				Assert.Equal (cancel, btn.Text);
+				Assert.Equal (one, total);
 
-			totalbtn.Clicked += (s, e) => {
-				MessageBox.Query ("Count", $"Count is {total}", "Ok");
-			};
+				RunSql ();
+			} finally {
+				SetReadyToRun ();
+			}
+		}).ContinueWith (async (s, e) => {
 
-			startWindow.Add (btn);
-			startWindow.Add (totalbtn);
+			await Task.Delay (1000);
+			Assert.Equal (clickMe, btn.Text);
+			Assert.Equal (three, total);
 
-			Application.Run (startWindow);
+			Interlocked.Increment (ref total);
 
 			Assert.Equal (clickMe, btn.Text);
 			Assert.Equal (four, total);
 
-			Application.RequestStop ();
-		}
+			taskCompleted = true;
 
-		private static async void RunAsyncTest (object sender, EventArgs e)
-		{
-			Assert.Equal (clickMe, btn.Text);
-			Assert.Equal (zero, total);
+		}, TaskScheduler.FromCurrentSynchronizationContext ());
+	}
+
+	private static void RunSql ()
+	{
+		Thread.Sleep (100);
+		Assert.Equal (cancel, btn.Text);
+		Assert.Equal (one, total);
 
-			btn.Text = "Cancel";
+		Application.Invoke (() => {
+			btn.Text = "Pew Pew";
 			Interlocked.Increment (ref total);
 			btn.SetNeedsDisplay ();
+		});
+	}
 
-			await Task.Run (() => {
-				try {
-					Assert.Equal (cancel, btn.Text);
-					Assert.Equal (one, total);
-
-					RunSql ();
-				} finally {
-					SetReadyToRun ();
-				}
-			}).ContinueWith (async (s, e) => {
-
-				await Task.Delay (1000);
-				Assert.Equal (clickMe, btn.Text);
-				Assert.Equal (three, total);
-
-				Interlocked.Increment (ref total);
-
-				Assert.Equal (clickMe, btn.Text);
-				Assert.Equal (four, total);
-
-				taskCompleted = true;
-
-			}, TaskScheduler.FromCurrentSynchronizationContext ());
-		}
+	private static void SetReadyToRun ()
+	{
+		Thread.Sleep (100);
+		Assert.Equal (pewPew, btn.Text);
+		Assert.Equal (two, total);
 
-		private static void RunSql ()
-		{
-			Thread.Sleep (100);
-			Assert.Equal (cancel, btn.Text);
-			Assert.Equal (one, total);
-
-			Application.MainLoop.Invoke (() => {
-				btn.Text = "Pew Pew";
-				Interlocked.Increment (ref total);
-				btn.SetNeedsDisplay ();
-			});
-		}
-
-		private static void SetReadyToRun ()
-		{
-			Thread.Sleep (100);
-			Assert.Equal (pewPew, btn.Text);
-			Assert.Equal (two, total);
-
-			Application.MainLoop.Invoke (() => {
-				btn.Text = "Click Me";
-				Interlocked.Increment (ref total);
-				btn.SetNeedsDisplay ();
-			});
-		}
+		Application.Invoke (() => {
+			btn.Text = "Click Me";
+			Interlocked.Increment (ref total);
+			btn.SetNeedsDisplay ();
+		});
 	}
 }

+ 2 - 2
UnitTests/Application/SynchronizatonContextTests.cs

@@ -31,7 +31,7 @@ namespace Terminal.Gui.ApplicationTests {
 						success = true;
 
 						// then tell the application to quit
-						Application.MainLoop.Invoke (() => Application.RequestStop ());
+						Application.Invoke (() => Application.RequestStop ());
 					}, null);
 				Assert.False (success);
 			});
@@ -56,7 +56,7 @@ namespace Terminal.Gui.ApplicationTests {
 						success = true;
 
 						// then tell the application to quit
-						Application.MainLoop.Invoke (() => Application.RequestStop ());
+						Application.Invoke (() => Application.RequestStop ());
 					}, null);
 				Assert.True (success);
 			});

+ 8 - 8
UnitTests/Clipboard/ClipboardTests.cs

@@ -36,7 +36,7 @@ namespace Terminal.Gui.ClipboardTests {
 			var clipText = "The Contents_Gets_Sets unit test pasted this to the OS clipboard.";
 			Clipboard.Contents = clipText;
 
-			Application.Iteration += () => Application.RequestStop ();
+			Application.Iteration += (s, a) => Application.RequestStop ();
 			Application.Run ();
 
 			Assert.Equal (clipText, Clipboard.Contents);
@@ -53,7 +53,7 @@ namespace Terminal.Gui.ClipboardTests {
 			var clipText = "The Contents_Gets_Sets unit test pasted this to the OS clipboard.";
 			Clipboard.Contents = clipText;
 
-			Application.Iteration += () => Application.RequestStop ();
+			Application.Iteration += (s, a) => Application.RequestStop ();
 			Application.Run ();
 
 			Assert.Equal (clipText, Clipboard.Contents);
@@ -71,7 +71,7 @@ namespace Terminal.Gui.ClipboardTests {
 			var clipText = "The Contents_Gets_Sets unit test pasted this to the OS clipboard.";
 			Clipboard.Contents = clipText;
 
-			Application.Iteration += () => Application.RequestStop ();
+			Application.Iteration += (s, a) => Application.RequestStop ();
 			Application.Run ();
 
 			Assert.Equal (clipText, Clipboard.Contents);
@@ -89,7 +89,7 @@ namespace Terminal.Gui.ClipboardTests {
 			var clipText = "The Contents_Gets_Sets unit test pasted this to the OS clipboard.";
 			Clipboard.Contents = clipText;
 
-			Application.Iteration += () => Application.RequestStop ();
+			Application.Iteration += (s, a) => Application.RequestStop ();
 			Application.Run ();
 
 			Assert.Equal (clipText, Clipboard.Contents);
@@ -108,7 +108,7 @@ namespace Terminal.Gui.ClipboardTests {
 			var clipText = "The TryGetClipboardData_Gets_From_OS_Clipboard unit test pasted this to the OS clipboard.";
 			Clipboard.Contents = clipText;
 
-			Application.Iteration += () => Application.RequestStop ();
+			Application.Iteration += (s, a) => Application.RequestStop ();
 
 			Application.Run ();
 
@@ -128,7 +128,7 @@ namespace Terminal.Gui.ClipboardTests {
 			if (Clipboard.IsSupported) Assert.True (Clipboard.TrySetClipboardData (clipText));
 			else Assert.False (Clipboard.TrySetClipboardData (clipText));
 
-			Application.Iteration += () => Application.RequestStop ();
+			Application.Iteration += (s, a) => Application.RequestStop ();
 
 			Application.Run ();
 
@@ -150,7 +150,7 @@ namespace Terminal.Gui.ClipboardTests {
 			var failed = false;
 			var getClipText = "";
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				int exitCode = 0;
 				string result = "";
 				output.WriteLine ($"Pasting to OS clipboard: {clipText}...");
@@ -221,7 +221,7 @@ namespace Terminal.Gui.ClipboardTests {
 			var clipReadText = "";
 			var failed = false;
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				Clipboard.Contents = clipText;
 
 				int exitCode = 0;

+ 0 - 3
UnitTests/Configuration/ConfigurationMangerTests.cs

@@ -3,12 +3,9 @@ using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Reflection;
-using System.Reflection.Metadata;
 using System.Text.Json;
-using Terminal.Gui;
 using Xunit;
 using static Terminal.Gui.ConfigurationManager;
-using Attribute = Terminal.Gui.Attribute;
 
 namespace Terminal.Gui.ConfigurationTests {
 	public class ConfigurationManagerTests {

+ 5 - 5
UnitTests/ConsoleDrivers/AddRuneTests.cs

@@ -20,7 +20,7 @@ public class AddRuneTests {
 
 		var driver = new FakeDriver ();
 		Application.Init (driver);
-		driver.Init (() => { });
+		driver.Init ();
 
 		driver.AddRune (new Rune ('a'));
 		Assert.Equal ((Rune)'a', driver.Contents [0, 0].Runes [0]);
@@ -34,7 +34,7 @@ public class AddRuneTests {
 	{
 		var driver = new FakeDriver ();
 		Application.Init (driver);
-		driver.Init (() => { });
+		driver.Init ();
 
 		driver.Move (driver.Cols, driver.Rows);
 		driver.AddRune ('a');
@@ -54,7 +54,7 @@ public class AddRuneTests {
 	{
 		var driver = new FakeDriver ();
 		Application.Init (driver);
-		driver.Init (() => { });
+		driver.Init ();
 
 		driver.AddRune ('a');
 		Assert.Equal ((Rune)'a', driver.Contents [0, 0].Runes [0]);
@@ -95,7 +95,7 @@ public class AddRuneTests {
 	{
 		var driver = new FakeDriver ();
 		Application.Init (driver);
-		driver.Init (() => { });
+		driver.Init ();
 
 		// 🍕 Slice of Pizza "\U0001F355"
 		var operationStatus = Rune.DecodeFromUtf16 ("\U0001F355", out Rune rune, out int charsConsumed);
@@ -143,7 +143,7 @@ public class AddRuneTests {
 	{
 		var driver = new FakeDriver ();
 		Application.Init (driver);
-		driver.Init (() => { });
+		driver.Init ();
 
 		var expected = new Rune ('ắ');
 

+ 13 - 13
UnitTests/ConsoleDrivers/ConsoleDriverTests.cs

@@ -27,7 +27,7 @@ namespace Terminal.Gui.DriverTests {
 		{
 			var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
 			Application.Init (driver);
-			driver.Init (() => { });
+			driver.Init ();
 
 			Assert.Equal (80, Console.BufferWidth);
 			Assert.Equal (25, Console.BufferHeight);
@@ -50,7 +50,7 @@ namespace Terminal.Gui.DriverTests {
 		{
 			var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
 			Application.Init (driver);
-			driver.Init (() => { });
+			driver.Init ();
 
 			Console.ForegroundColor = ConsoleColor.Red;
 			Assert.Equal (ConsoleColor.Red, Console.ForegroundColor);
@@ -81,12 +81,12 @@ namespace Terminal.Gui.DriverTests {
 			var count = 0;
 			var wasKeyPressed = false;
 
-			view.KeyPress += (s, e) => {
+			view.KeyPressed += (s, e) => {
 				wasKeyPressed = true;
 			};
 			top.Add (view);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				count++;
 				if (count == 10) Application.RequestStop ();
 			};
@@ -120,7 +120,7 @@ namespace Terminal.Gui.DriverTests {
 			var rText = "";
 			var idx = 0;
 
-			view.KeyPress += (s, e) => {
+			view.KeyPressed += (s, e) => {
 				Assert.Equal (text [idx], (char)e.KeyEvent.Key);
 				rText += (char)e.KeyEvent.Key;
 				Assert.Equal (rText, text.Substring (0, idx + 1));
@@ -129,7 +129,7 @@ namespace Terminal.Gui.DriverTests {
 			};
 			top.Add (view);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				if (mKeys.Count == 0) Application.RequestStop ();
 			};
 
@@ -160,7 +160,7 @@ namespace Terminal.Gui.DriverTests {
 		//		return false;
 		//	};
 		//	output.WriteLine ($"Add timeout to simulate key presses after {quitTime}ms");
-		//	_ = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (quitTime), closeCallback);
+		//	_ = Application.AddTimeout (TimeSpan.FromMilliseconds (quitTime), closeCallback);
 
 		//	// If Top doesn't quit within abortTime * 5 (500ms), this will force it
 		//	uint abortTime = quitTime * 5;
@@ -170,7 +170,7 @@ namespace Terminal.Gui.DriverTests {
 		//		return false;
 		//	};
 		//	output.WriteLine ($"Add timeout to force quit after {abortTime}ms");
-		//	_ = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (abortTime), forceCloseCallback);
+		//	_ = Application.AddTimeout (TimeSpan.FromMilliseconds (abortTime), forceCloseCallback);
 
 		//	Key key = Key.Unknown;
 			
@@ -182,7 +182,7 @@ namespace Terminal.Gui.DriverTests {
 		//	};
 
 		//	int iterations = 0;
-		//	Application.Iteration += () => {
+		//	Application.Iteration += (s, a) => {
 		//		output.WriteLine ($"  iteration {++iterations}");
 
 		//		if (Console.MockKeyPresses.Count == 0) {
@@ -204,10 +204,10 @@ namespace Terminal.Gui.DriverTests {
 			var driver = (FakeDriver)Activator.CreateInstance (driverType);
 			Application.Init (driver);
 			var wasTerminalResized = false;
-			Application.TerminalResized = (e) => {
+			Application.SizeChanging += (s, e) => {
 				wasTerminalResized = true;
-				Assert.Equal (120, e.Cols);
-				Assert.Equal (40, e.Rows);
+				Assert.Equal (120, e.Size.Width);
+				Assert.Equal (40, e.Size.Height);
 			};
 
 			Assert.Equal (80, Console.BufferWidth);
@@ -238,7 +238,7 @@ namespace Terminal.Gui.DriverTests {
 
 //			System.Threading.Tasks.Task.Run (() => {
 //				System.Threading.Tasks.Task.Delay (500).Wait ();
-//				Application.MainLoop.Invoke (() => {
+//				Application.Invoke (() => {
 //					var lbl = new Label ("Hello World") { X = Pos.Center () };
 //					var dlg = new Dialog ();
 //					dlg.Add (lbl);

+ 2 - 2
UnitTests/ConsoleDrivers/DriverColorTests.cs

@@ -47,7 +47,7 @@ namespace Terminal.Gui.DriverTests {
 		public void SupportsTrueColor_Defaults (Type driverType, bool expectedSetting)
 		{
 			var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
-			driver.Init (() => { });
+			driver.Init ();
 
 			Assert.Equal (expectedSetting, driver.SupportsTrueColor);
 
@@ -65,7 +65,7 @@ namespace Terminal.Gui.DriverTests {
 		public void Force16Colors_Sets (Type driverType)
 		{
 			var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
-			driver.Init (() => { });
+			driver.Init ();
 
 			driver.Force16Colors = true;
 			Assert.True (driver.Force16Colors);

+ 2 - 2
UnitTests/ConsoleDrivers/KeyTests.cs

@@ -208,7 +208,7 @@ namespace Terminal.Gui.InputTests {
 
 				var top = Application.Top;
 
-				top.KeyPress += (s, e) => {
+				top.KeyPressed += (s, e) => {
 					var after = ShortcutHelper.GetModifiersKey (e.KeyEvent);
 					Assert.Equal (expectedRemapping, after);
 					e.Handled = true;
@@ -217,7 +217,7 @@ namespace Terminal.Gui.InputTests {
 
 				var iterations = -1;
 
-				Application.Iteration += () => {
+				Application.Iteration += (s, a) => {
 					iterations++;
 					if (iterations == 0) Application.Driver.SendKeys ((char)mappedConsoleKey, ConsoleKey.Packet, shift, alt, control);
 				};

+ 270 - 0
UnitTests/ConsoleDrivers/MainLoopDriverTests.cs

@@ -0,0 +1,270 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using Xunit;
+using Xunit.Abstractions;
+
+// Alias Console to MockConsole so we don't accidentally use Console
+using Console = Terminal.Gui.FakeConsole;
+
+namespace Terminal.Gui.DriverTests;
+
+public class MainLoopDriverTests {
+
+	public MainLoopDriverTests (ITestOutputHelper output)
+	{
+		ConsoleDriver.RunningUnitTests = true;
+	}
+
+	[Theory]
+	[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
+	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
+	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
+	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	public void MainLoop_Constructs_Disposes (Type driverType, Type mainLoopDriverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver });
+		var mainLoop = new MainLoop (mainLoopDriver);
+
+		// Check default values
+		Assert.NotNull (mainLoop);
+		Assert.Equal (mainLoopDriver, mainLoop.MainLoopDriver);
+		Assert.Empty (mainLoop.IdleHandlers);
+		Assert.Empty (mainLoop.Timeouts);
+		Assert.False (mainLoop.Running);
+
+		// Clean up
+		mainLoop.Dispose ();
+		// TODO: It'd be nice if we could really verify IMainLoopDriver.TearDown was called
+		// and that it was actually cleaned up.
+		Assert.Null (mainLoop.MainLoopDriver);
+		Assert.Empty (mainLoop.IdleHandlers);
+		Assert.Empty (mainLoop.Timeouts);
+		Assert.False (mainLoop.Running);
+	}
+	
+	[Theory]
+	[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
+	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
+	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
+	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	public void MainLoop_AddTimeout_ValidParameters_ReturnsToken (Type driverType, Type mainLoopDriverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver });
+		var mainLoop = new MainLoop (mainLoopDriver);
+		var callbackInvoked = false;
+
+		var token = mainLoop.AddTimeout (TimeSpan.FromMilliseconds (100), () => {
+			callbackInvoked = true;
+			return false;
+		});
+
+		Assert.NotNull (token);
+		mainLoop.RunIteration (); // Run an iteration to process the timeout
+		Assert.False (callbackInvoked); // Callback should not be invoked immediately
+		Thread.Sleep (200); // Wait for the timeout
+		mainLoop.RunIteration (); // Run an iteration to process the timeout
+		Assert.True (callbackInvoked); // Callback should be invoked after the timeout
+		mainLoop.Dispose ();
+	}
+
+	[Theory]
+	[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
+	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
+	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
+	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	public void MainLoop_RemoveTimeout_ValidToken_ReturnsTrue (Type driverType, Type mainLoopDriverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver });
+		var mainLoop = new MainLoop (mainLoopDriver);
+
+		var token = mainLoop.AddTimeout (TimeSpan.FromMilliseconds (100), () => false);
+		var result = mainLoop.RemoveTimeout (token);
+
+		Assert.True (result);
+		mainLoop.Dispose ();
+	}
+
+	[Theory]
+	[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
+	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
+	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
+	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	public void MainLoop_RemoveTimeout_InvalidToken_ReturnsFalse (Type driverType, Type mainLoopDriverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver });
+		var mainLoop = new MainLoop (mainLoopDriver);
+
+		var result = mainLoop.RemoveTimeout (new object ());
+
+		Assert.False (result);
+	}
+
+	[Theory]
+	[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
+	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
+	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
+	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	public void MainLoop_AddIdle_ValidIdleHandler_ReturnsToken (Type driverType, Type mainLoopDriverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver });
+		var mainLoop = new MainLoop (mainLoopDriver);
+		var idleHandlerInvoked = false;
+
+		bool IdleHandler ()
+		{
+			idleHandlerInvoked = true;
+			return false;
+		}
+
+		Func<bool> token = mainLoop.AddIdle (IdleHandler);
+
+		Assert.NotNull (token);
+		Assert.False (idleHandlerInvoked); // Idle handler should not be invoked immediately
+		mainLoop.RunIteration (); // Run an iteration to process the idle handler
+		Assert.True (idleHandlerInvoked); // Idle handler should be invoked after processing
+		mainLoop.Dispose ();
+	}
+
+	[Theory]
+	[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
+	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
+	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
+	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	public void MainLoop_RemoveIdle_ValidToken_ReturnsTrue (Type driverType, Type mainLoopDriverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver });
+		var mainLoop = new MainLoop (mainLoopDriver);
+
+		bool IdleHandler () => false;
+		Func<bool> token = mainLoop.AddIdle (IdleHandler);
+		var result = mainLoop.RemoveIdle (token);
+
+		Assert.True (result);
+		mainLoop.Dispose ();
+	}
+
+	[Theory]
+	[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
+	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
+	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
+	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	public void MainLoop_RemoveIdle_InvalidToken_ReturnsFalse (Type driverType, Type mainLoopDriverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver });
+		var mainLoop = new MainLoop (mainLoopDriver);
+
+		var result = mainLoop.RemoveIdle (() => false);
+
+		Assert.False (result);
+		mainLoop.Dispose ();
+	}
+
+	[Theory]
+	[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
+	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
+	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
+	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	public void MainLoop_RunIteration_ValidIdleHandler_CallsIdleHandler (Type driverType, Type mainLoopDriverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver });
+		var mainLoop = new MainLoop (mainLoopDriver);
+		var idleHandlerInvoked = false;
+
+		Func<bool> idleHandler = () => {
+			idleHandlerInvoked = true;
+			return false;
+		};
+
+		mainLoop.AddIdle (idleHandler);
+		mainLoop.RunIteration (); // Run an iteration to process the idle handler
+
+		Assert.True (idleHandlerInvoked);
+		mainLoop.Dispose ();
+	}
+
+	[Theory]
+	[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
+	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
+	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
+	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	public void MainLoop_CheckTimersAndIdleHandlers_NoTimersOrIdleHandlers_ReturnsFalse (Type driverType, Type mainLoopDriverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver });
+		var mainLoop = new MainLoop (mainLoopDriver);
+
+		var result = mainLoop.CheckTimersAndIdleHandlers (out var waitTimeout);
+
+		Assert.False (result);
+		Assert.Equal (-1, waitTimeout);
+		mainLoop.Dispose ();
+	}
+
+	[Theory]
+	[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
+	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
+	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
+	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	public void MainLoop_CheckTimersAndIdleHandlers_TimersActive_ReturnsTrue (Type driverType, Type mainLoopDriverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver });
+		var mainLoop = new MainLoop (mainLoopDriver);
+
+		mainLoop.AddTimeout (TimeSpan.FromMilliseconds (100), () => false);
+		var result = mainLoop.CheckTimersAndIdleHandlers (out var waitTimeout);
+
+		Assert.True (result);
+		Assert.True (waitTimeout >= 0);
+		mainLoop.Dispose ();
+	}
+
+	[Theory]
+	[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
+	[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
+	[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
+	[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	public void MainLoop_CheckTimersAndIdleHandlers_IdleHandlersActive_ReturnsTrue (Type driverType, Type mainLoopDriverType)
+	{
+		var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+		var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver });
+		var mainLoop = new MainLoop (mainLoopDriver);
+
+		mainLoop.AddIdle (() => false);
+		var result = mainLoop.CheckTimersAndIdleHandlers (out var waitTimeout);
+
+		Assert.True (result);
+		Assert.Equal (-1, waitTimeout);
+		mainLoop.Dispose ();
+	}
+
+	//[Theory]
+	//[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
+	//[InlineData (typeof (NetDriver), typeof (NetMainLoop))]
+	//[InlineData (typeof (CursesDriver), typeof (UnixMainLoop))]
+	//[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
+	//public void MainLoop_Invoke_ValidAction_RunsAction (Type driverType, Type mainLoopDriverType)
+	//{
+	//	var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
+	//	var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver });
+	//	var mainLoop = new MainLoop (mainLoopDriver);
+	//	var actionInvoked = false;
+
+	//	mainLoop.Invoke (() => { actionInvoked = true; });
+	//	mainLoop.RunIteration (); // Run an iteration to process the action.
+
+	//	Assert.True (actionInvoked);
+	//	mainLoop.Dispose ();
+	//}
+}

+ 13 - 13
UnitTests/Dialogs/DialogTests.cs

@@ -148,7 +148,7 @@ namespace Terminal.Gui.DialogTests {
 			Application.Top.BorderStyle = LineStyle.Double;
 
 			var iterations = -1;
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 
 				if (iterations == 0) {
@@ -352,7 +352,7 @@ namespace Terminal.Gui.DialogTests {
 			button2 = new Button (btn2Text);
 			(runstate, dlg) = RunButtonTestDialog (title, width, Dialog.ButtonAlignments.Center, button1, button2);
 			button1.Visible = false;
-			Application.RunMainLoopIteration (ref runstate, ref firstIteration);
+			Application.RunIteration (ref runstate, ref firstIteration);
 			buttonRow = $@"{CM.Glyphs.VLine}         {btn2} {CM.Glyphs.VLine}";
 			TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", output);
 			Application.End (runstate);
@@ -363,7 +363,7 @@ namespace Terminal.Gui.DialogTests {
 			button2 = new Button (btn2Text);
 			(runstate, dlg) = RunButtonTestDialog (title, width, Dialog.ButtonAlignments.Justify, button1, button2);
 			button1.Visible = false;
-			Application.RunMainLoopIteration (ref runstate, ref firstIteration);
+			Application.RunIteration (ref runstate, ref firstIteration);
 			buttonRow = $@"{CM.Glyphs.VLine}          {btn2}{CM.Glyphs.VLine}";
 			TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", output);
 			Application.End (runstate);
@@ -374,7 +374,7 @@ namespace Terminal.Gui.DialogTests {
 			button2 = new Button (btn2Text);
 			(runstate, dlg) = RunButtonTestDialog (title, width, Dialog.ButtonAlignments.Right, button1, button2);
 			button1.Visible = false;
-			Application.RunMainLoopIteration (ref runstate, ref firstIteration);
+			Application.RunIteration (ref runstate, ref firstIteration);
 			TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", output);
 			Application.End (runstate);
 
@@ -384,7 +384,7 @@ namespace Terminal.Gui.DialogTests {
 			button2 = new Button (btn2Text);
 			(runstate, dlg) = RunButtonTestDialog (title, width, Dialog.ButtonAlignments.Left, button1, button2);
 			button1.Visible = false;
-			Application.RunMainLoopIteration (ref runstate, ref firstIteration);
+			Application.RunIteration (ref runstate, ref firstIteration);
 			buttonRow = $@"{CM.Glyphs.VLine}        {btn2}  {CM.Glyphs.VLine}";
 			TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", output);
 			Application.End (runstate);
@@ -713,7 +713,7 @@ namespace Terminal.Gui.DialogTests {
 			buttonRow = $"{CM.Glyphs.VLine} {btn1} {btn2} {CM.Glyphs.VLine}";
 			dlg.AddButton (new Button (btn2Text));
 			bool first = false;
-			Application.RunMainLoopIteration (ref runstate, ref first);
+			Application.RunIteration (ref runstate, ref first);
 			TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", output);
 			Application.End (runstate);
 
@@ -729,7 +729,7 @@ namespace Terminal.Gui.DialogTests {
 			buttonRow = $"{CM.Glyphs.VLine}{btn1}   {btn2}{CM.Glyphs.VLine}";
 			dlg.AddButton (new Button (btn2Text));
 			first = false;
-			Application.RunMainLoopIteration (ref runstate, ref first);
+			Application.RunIteration (ref runstate, ref first);
 			TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", output);
 			Application.End (runstate);
 
@@ -745,7 +745,7 @@ namespace Terminal.Gui.DialogTests {
 			buttonRow = $"{CM.Glyphs.VLine}  {btn1} {btn2}{CM.Glyphs.VLine}";
 			dlg.AddButton (new Button (btn2Text));
 			first = false;
-			Application.RunMainLoopIteration (ref runstate, ref first);
+			Application.RunIteration (ref runstate, ref first);
 			TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", output);
 			Application.End (runstate);
 
@@ -761,7 +761,7 @@ namespace Terminal.Gui.DialogTests {
 			buttonRow = $"{CM.Glyphs.VLine}{btn1} {btn2}  {CM.Glyphs.VLine}";
 			dlg.AddButton (new Button (btn2Text));
 			first = false;
-			Application.RunMainLoopIteration (ref runstate, ref first);
+			Application.RunIteration (ref runstate, ref first);
 			TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", output);
 			Application.End (runstate);
 		}
@@ -796,7 +796,7 @@ namespace Terminal.Gui.DialogTests {
 			var btn = $"{CM.Glyphs.LeftBracket}{CM.Glyphs.LeftDefaultIndicator} Ok {CM.Glyphs.RightDefaultIndicator}{CM.Glyphs.RightBracket}";
 
 			var iterations = -1;
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 				if (iterations == 0) {
 					Assert.True (btn1.ProcessKey (new KeyEvent (Key.Enter, new KeyModifiers ())));
@@ -876,7 +876,7 @@ namespace Terminal.Gui.DialogTests {
 			var win = new Window ();
 
 			int iterations = 0;
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				if (++iterations > 2) {
 					Application.RequestStop ();
 				}
@@ -913,7 +913,7 @@ namespace Terminal.Gui.DialogTests {
 		//			((FakeDriver)Application.Driver).SetBufferSize (20, height);
 		//			var win = new Window ();
 
-		//			Application.Iteration += () => {
+		//			Application.Iteration += (s, a) => {
 		//				var dlg = new Dialog ("Test", new Button ("Ok"));
 
 		//				dlg.LayoutComplete += (s, a) => {
@@ -946,7 +946,7 @@ namespace Terminal.Gui.DialogTests {
 			var win = new Window ();
 
 			int iterations = 0;
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				if (++iterations > 2) {
 					Application.RequestStop ();
 				}

+ 14 - 14
UnitTests/Dialogs/MessageBoxTests.cs

@@ -23,7 +23,7 @@ namespace Terminal.Gui.DialogTests {
 			Application.Begin (Application.Top);
 			((FakeDriver)Application.Driver).SetBufferSize (100, 100);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 
 				if (iterations == 0) {
@@ -52,7 +52,7 @@ namespace Terminal.Gui.DialogTests {
 			Application.Begin (Application.Top);
 			((FakeDriver)Application.Driver).SetBufferSize (100, 100);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 
 				if (iterations == 0) {
@@ -87,7 +87,7 @@ namespace Terminal.Gui.DialogTests {
 			Application.Begin (Application.Top);
 			((FakeDriver)Application.Driver).SetBufferSize (100, 100);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 
 				if (iterations == 0) {
@@ -121,7 +121,7 @@ namespace Terminal.Gui.DialogTests {
 			Application.Begin (Application.Top);
 			((FakeDriver)Application.Driver).SetBufferSize (100, 100);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 
 				if (iterations == 0) {
@@ -155,7 +155,7 @@ namespace Terminal.Gui.DialogTests {
 			Application.Begin (Application.Top);
 			((FakeDriver)Application.Driver).SetBufferSize (100, 100);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 
 				if (iterations == 0) {
@@ -179,7 +179,7 @@ namespace Terminal.Gui.DialogTests {
 			var iterations = -1;
 			Application.Begin (Application.Top);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 
 				if (iterations == 0) {
@@ -219,7 +219,7 @@ namespace Terminal.Gui.DialogTests {
 
 			((FakeDriver)Application.Driver).SetBufferSize (40 + 4, 8);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 
 				if (iterations == 0) {
@@ -253,7 +253,7 @@ namespace Terminal.Gui.DialogTests {
 			var iterations = -1;
 			Application.Begin (Application.Top);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 
 				if (iterations == 0) {
@@ -287,7 +287,7 @@ namespace Terminal.Gui.DialogTests {
 			Application.Begin (Application.Top);
 			var btn = $"{CM.Glyphs.LeftBracket}{CM.Glyphs.LeftDefaultIndicator} Ok {CM.Glyphs.RightDefaultIndicator}{CM.Glyphs.RightBracket}";
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 
 				if (iterations == 0) {
@@ -320,7 +320,7 @@ namespace Terminal.Gui.DialogTests {
 			((FakeDriver)Application.Driver).SetBufferSize (20, 10);
 
 			var btn = $"{CM.Glyphs.LeftBracket}{CM.Glyphs.LeftDefaultIndicator} btn {CM.Glyphs.RightDefaultIndicator}{CM.Glyphs.RightBracket}";
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 
 				if (iterations == 0) {
@@ -377,7 +377,7 @@ namespace Terminal.Gui.DialogTests {
 
 			var btn = $"{CM.Glyphs.LeftBracket}{CM.Glyphs.LeftDefaultIndicator} btn {CM.Glyphs.RightDefaultIndicator}{CM.Glyphs.RightBracket}";
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 
 				if (iterations == 0) {
@@ -435,7 +435,7 @@ namespace Terminal.Gui.DialogTests {
 
 			var btn = $"{CM.Glyphs.LeftBracket}{CM.Glyphs.LeftDefaultIndicator} btn {CM.Glyphs.RightDefaultIndicator}{CM.Glyphs.RightBracket}";
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 
 				if (iterations == 0) {
@@ -490,7 +490,7 @@ namespace Terminal.Gui.DialogTests {
 			((FakeDriver)Application.Driver).SetBufferSize (20, 10);
 			var btn = $"{CM.Glyphs.LeftBracket}{CM.Glyphs.LeftDefaultIndicator} btn {CM.Glyphs.RightDefaultIndicator}{CM.Glyphs.RightBracket}";
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 
 				if (iterations == 0) {
@@ -552,7 +552,7 @@ namespace Terminal.Gui.DialogTests {
 			var iterations = -1;
 			Application.Begin (Application.Top);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 
 				if (iterations == 0) {

+ 5 - 5
UnitTests/Dialogs/WizardTests.cs

@@ -163,7 +163,7 @@ namespace Terminal.Gui.DialogTests {
 			//wizard.LayoutSubviews ();
 			var firstIteration = false;
 			var runstate = Application.Begin (wizard);
-			Application.RunMainLoopIteration (ref runstate, ref firstIteration);
+			Application.RunIteration (ref runstate, ref firstIteration);
 
 			TestHelpers.AssertDriverContentsWithFrameAre ($"{topRow}\n{row2}\n{row3}\n{row4}\n{separatorRow}\n{buttonRow}\n{bottomRow}", output);
 			Application.End (runstate);
@@ -530,10 +530,10 @@ namespace Terminal.Gui.DialogTests {
 
 			var runstate = Application.Begin (wizard);
 			var firstIteration = true;
-			Application.RunMainLoopIteration (ref runstate, ref firstIteration);
+			Application.RunIteration (ref runstate, ref firstIteration);
 
 			wizard.NextFinishButton.OnClicked ();
-			Application.RunMainLoopIteration (ref runstate, ref firstIteration);
+			Application.RunIteration (ref runstate, ref firstIteration);
 			Application.End (runstate);
 			Assert.True (finishedFired);
 			Assert.True (closedFired);
@@ -559,7 +559,7 @@ namespace Terminal.Gui.DialogTests {
 			};
 
 			runstate = Application.Begin (wizard);
-			Application.RunMainLoopIteration (ref runstate, ref firstIteration);
+			Application.RunIteration (ref runstate, ref firstIteration);
 
 			Assert.Equal (step1.Title, wizard.CurrentStep.Title);
 			wizard.NextFinishButton.OnClicked ();
@@ -597,7 +597,7 @@ namespace Terminal.Gui.DialogTests {
 			};
 
 			runstate = Application.Begin (wizard);
-			Application.RunMainLoopIteration (ref runstate, ref firstIteration);
+			Application.RunIteration (ref runstate, ref firstIteration);
 
 			Assert.Equal (step2.Title, wizard.CurrentStep.Title);
 			Assert.Equal (wizard.GetLastStep ().Title, wizard.CurrentStep.Title);

+ 3 - 3
UnitTests/Drawing/AttributeTests.cs

@@ -91,7 +91,7 @@ public class AttributeTests {
 	{
 		var driver = new FakeDriver ();
 		Application.Init (driver);
-		driver.Init (() => { });
+		driver.Init ();
 
 		// Test parameterless constructor
 		var attr = new Attribute ();
@@ -197,7 +197,7 @@ public class AttributeTests {
 	{
 		var driver = new FakeDriver ();
 		Application.Init (driver);
-		driver.Init (() => { });
+		driver.Init ();
 
 		var attr = new Attribute ();
 
@@ -238,7 +238,7 @@ public class AttributeTests {
 	{
 		var driver = new FakeDriver ();
 		Application.Init (driver);
-		driver.Init (() => { });
+		driver.Init ();
 
 		var fg = new Color ();
 		fg = new Color (Color.Red);

+ 11 - 17
UnitTests/Input/EscSeqUtilsTests.cs

@@ -519,14 +519,11 @@ namespace Terminal.Gui.InputTests {
 			Application.Top.Add (view);
 			Application.Begin (Application.Top);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 0,
-					Y = 0,
-					Flags = 0
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 0,
+				Y = 0,
+				Flags = 0
+			}));
 
 			ClearAll ();
 			cki = new ConsoleKeyInfo [] {
@@ -558,18 +555,15 @@ namespace Terminal.Gui.InputTests {
 			Assert.Equal (new Point (1, 2), pos);
 			Assert.False (isReq);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				if (actionStarted) {
 					// set Application.WantContinuousButtonPressedView to null
 					view.WantContinuousButtonPressed = false;
-					ReflectionTools.InvokePrivate (
-						typeof (Application),
-						"ProcessMouseEvent",
-						new MouseEvent () {
-							X = 0,
-							Y = 0,
-							Flags = 0
-						});
+					Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+						X = 0,
+						Y = 0,
+						Flags = 0
+					}));
 
 					Application.RequestStop ();
 				}

+ 0 - 35
UnitTests/ReflectionTools.cs

@@ -1,35 +0,0 @@
-using System;
-using System.Reflection;
-
-public static class ReflectionTools {
-	// If the class is non-static
-	public static Object InvokePrivate (Object objectUnderTest, string method, params object [] args)
-	{
-		Type t = objectUnderTest.GetType ();
-		return t.InvokeMember (method,
-		    BindingFlags.InvokeMethod |
-		    BindingFlags.NonPublic |
-		    BindingFlags.Instance |
-		    BindingFlags.Static,
-		    null,
-		    objectUnderTest,
-		    args);
-	}
-	// if the class is static
-	public static Object InvokePrivate (Type typeOfObjectUnderTest, string method, params object [] args)
-	{
-		MemberInfo [] members = typeOfObjectUnderTest.GetMembers (BindingFlags.NonPublic | BindingFlags.Static);
-		foreach (var member in members) {
-			if (member.Name == method) {
-				return typeOfObjectUnderTest.InvokeMember (method,
-					BindingFlags.NonPublic |
-					BindingFlags.Static |
-					BindingFlags.InvokeMethod,
-					null,
-					typeOfObjectUnderTest,
-					args);
-			}
-		}
-		return null;
-	}
-}

+ 8 - 8
UnitTests/UICatalog/ScenarioTests.cs

@@ -69,7 +69,7 @@ namespace UICatalog.Tests {
 				FakeConsole.PushMockKeyPress (Application.QuitKey);
 
 				// The only key we care about is the QuitKey
-				Application.Top.KeyPress += (object sender, KeyEventEventArgs args) => {
+				Application.Top.KeyPressed += (object sender, KeyEventEventArgs args) => {
 					output.WriteLine ($"  Keypress: {args.KeyEvent.Key}");
 					// BUGBUG: (#2474) For some reason ReadKey is not returning the QuitKey for some Scenarios
 					// by adding this Space it seems to work.
@@ -79,7 +79,7 @@ namespace UICatalog.Tests {
 
 				uint abortTime = 500;
 				// If the scenario doesn't close within 500ms, this will force it to quit
-				Func<MainLoop, bool> forceCloseCallback = (MainLoop loop) => {
+				Func<bool> forceCloseCallback = () => {
 					if (Application.Top.Running && FakeConsole.MockKeyPresses.Count == 0) {
 						Application.RequestStop ();
 						// See #2474 for why this is commented out
@@ -88,9 +88,9 @@ namespace UICatalog.Tests {
 					return false;
 				};
 				//output.WriteLine ($"  Add timeout to force quit after {abortTime}ms");
-				_ = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (abortTime), forceCloseCallback);
+				_ = Application.AddTimeout (TimeSpan.FromMilliseconds (abortTime), forceCloseCallback);
 
-				Application.Iteration += () => {
+				Application.Iteration += (s, a) => {
 					//output.WriteLine ($"  iteration {++iterations}");
 					if (Application.Top.Running && FakeConsole.MockKeyPresses.Count == 0) {
 						Application.RequestStop ();
@@ -130,7 +130,7 @@ namespace UICatalog.Tests {
 
 			var ms = 100;
 			var abortCount = 0;
-			Func<MainLoop, bool> abortCallback = (MainLoop loop) => {
+			Func<bool> abortCallback = () => {
 				abortCount++;
 				output.WriteLine ($"'Generic' abortCount {abortCount}");
 				Application.RequestStop ();
@@ -139,7 +139,7 @@ namespace UICatalog.Tests {
 
 			int iterations = 0;
 			object token = null;
-			Application.Iteration = () => {
+			Application.Iteration += (s, a) => {
 				if (token == null) {
 					// Timeout only must start at first iteration
 					token = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (ms), abortCallback);
@@ -153,7 +153,7 @@ namespace UICatalog.Tests {
 				}
 			};
 
-			Application.Top.KeyPress += (object sender, KeyEventEventArgs args) => {
+			Application.Top.KeyPressed += (object sender, KeyEventEventArgs args) => {
 				// See #2474 for why this is commented out
 				Assert.Equal (Key.CtrlMask | Key.Q, args.KeyEvent.Key);
 			};
@@ -392,7 +392,7 @@ namespace UICatalog.Tests {
 
 			int iterations = 0;
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 
 				if (iterations < _viewClasses.Count) {

+ 8 - 8
UnitTests/View/FrameTests.cs

@@ -61,7 +61,7 @@ namespace Terminal.Gui.ViewTests {
 			bool firstIteration = false;
 
 			((FakeDriver)Application.Driver).SetBufferSize (20, height);
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			var expected = string.Empty;
 
 			switch (height) {
@@ -119,7 +119,7 @@ namespace Terminal.Gui.ViewTests {
 			bool firstIteration = false;
 
 			((FakeDriver)Application.Driver).SetBufferSize (width, 3);
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			var expected = string.Empty;
 
 			switch (width) {
@@ -210,7 +210,7 @@ namespace Terminal.Gui.ViewTests {
 			bool firstIteration = false;
 
 			((FakeDriver)Application.Driver).SetBufferSize (3, 3);
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			var expected = @"
 ┌─┐
 │ │
@@ -234,7 +234,7 @@ namespace Terminal.Gui.ViewTests {
 			bool firstIteration = false;
 
 			((FakeDriver)Application.Driver).SetBufferSize (5, 5);
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			var expected = @"
 ╔═══╗
 ║┌─┐║
@@ -262,7 +262,7 @@ namespace Terminal.Gui.ViewTests {
 			bool firstIteration = false;
 
 			((FakeDriver)Application.Driver).SetBufferSize (10, 4);
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			var expected = @"
 ╔════════╗
 ║┌┤1234├┐║
@@ -299,7 +299,7 @@ namespace Terminal.Gui.ViewTests {
 			bool firstIteration = false;
 
 			((FakeDriver)Application.Driver).SetBufferSize (width, 4);
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			var expected = string.Empty;
 
 			switch (width) {
@@ -411,7 +411,7 @@ namespace Terminal.Gui.ViewTests {
 			bool firstIteration = false;
 
 			((FakeDriver)Application.Driver).SetBufferSize (width, 4);
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			var expected = string.Empty;
 
 			switch (width) {
@@ -523,7 +523,7 @@ namespace Terminal.Gui.ViewTests {
 			bool firstIteration = false;
 
 			((FakeDriver)Application.Driver).SetBufferSize (width, 5);
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			var expected = string.Empty;
 
 			switch (width) {

+ 6 - 6
UnitTests/View/KeyboardTests.cs

@@ -25,14 +25,14 @@ namespace Terminal.Gui.ViewTests {
 			var top = Application.Top;
 
 			var text = new TextField ("");
-			text.KeyPress += (s, e) => {
+			text.KeyPressed += (s, e) => {
 				e.Handled = true;
 				Assert.True (e.Handled);
 				Assert.Equal (Key.N, e.KeyEvent.Key);
 			};
 			top.Add (text);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				Console.MockKeyPresses.Push (new ConsoleKeyInfo ('N', ConsoleKey.N, false, false, false));
 				Assert.Equal ("", text.Text);
 
@@ -61,7 +61,7 @@ namespace Terminal.Gui.ViewTests {
 				e.Handled = true;
 				keyDown = true;
 			};
-			view.KeyPress += (s, e) => {
+			view.KeyPressed += (s, e) => {
 				Assert.Equal (Key.a, e.KeyEvent.Key);
 				Assert.False (keyPress);
 				Assert.False (view.IsKeyPress);
@@ -80,7 +80,7 @@ namespace Terminal.Gui.ViewTests {
 
 			Console.MockKeyPresses.Push (new ConsoleKeyInfo ('a', ConsoleKey.A, false, false, false));
 
-			Application.Iteration += () => Application.RequestStop ();
+			Application.Iteration += (s, a) => Application.RequestStop ();
 
 			Assert.True (view.CanFocus);
 
@@ -145,7 +145,7 @@ namespace Terminal.Gui.ViewTests {
 				Assert.False (view.IsKeyDown);
 				keyDown = true;
 			};
-			view.KeyPress += (s, e) => {
+			view.KeyPressed += (s, e) => {
 				keyPress = true;
 			};
 			view.KeyUp += (s, e) => {
@@ -162,7 +162,7 @@ namespace Terminal.Gui.ViewTests {
 
 			Console.MockKeyPresses.Push (new ConsoleKeyInfo ('\0', 0, shift, alt, control));
 
-			Application.Iteration += () => Application.RequestStop ();
+			Application.Iteration += (s, a) => Application.RequestStop ();
 
 			Assert.True (view.CanFocus);
 

+ 6 - 6
UnitTests/View/Layout/DimTests.cs

@@ -298,7 +298,7 @@ namespace Terminal.Gui.ViewTests {
 				Assert.Equal (2, v.Height);
 			};
 
-			Application.Iteration += () => Application.RequestStop ();
+			Application.Iteration += (s, a) => Application.RequestStop ();
 
 			Application.Run ();
 		}
@@ -533,7 +533,7 @@ namespace Terminal.Gui.ViewTests {
 				Assert.Equal (38, v6.Frame.Height); // 198-7*20=18
 			};
 
-			Application.Iteration += () => Application.RequestStop ();
+			Application.Iteration += (s, a) => Application.RequestStop ();
 
 			Application.Run ();
 		}
@@ -702,7 +702,7 @@ namespace Terminal.Gui.ViewTests {
 				}
 			};
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				while (count < 20) field.OnKeyDown (new KeyEvent (Key.Enter, new KeyModifiers ()));
 
 				Application.RequestStop ();
@@ -1075,7 +1075,7 @@ namespace Terminal.Gui.ViewTests {
 				}
 			};
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				while (count < 21) {
 					field.OnKeyDown (new KeyEvent (Key.Enter, new KeyModifiers ()));
 					if (count == 20) {
@@ -1137,7 +1137,7 @@ namespace Terminal.Gui.ViewTests {
 				}
 			};
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				while (count > 0) field.OnKeyDown (new KeyEvent (Key.Enter, new KeyModifiers ()));
 
 				Application.RequestStop ();
@@ -1214,7 +1214,7 @@ namespace Terminal.Gui.ViewTests {
 				}
 			};
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				while (count > -1) {
 					field.OnKeyDown (new KeyEvent (Key.Enter, new KeyModifiers ()));
 					if (count == 0) {

+ 15 - 18
UnitTests/View/Layout/LayoutTests.cs

@@ -83,7 +83,7 @@ namespace Terminal.Gui.ViewTests {
 
 			var second = new View () { Id = "second" };
 			root.Add (second);
-			
+
 			second.X = Pos.Right (first) + 1;
 
 			root.LayoutSubviews ();
@@ -586,7 +586,7 @@ Y
 			{
 				var text = "";
 				for (int i = 0; i < 4; i++) {
-					text += Application.Driver.Contents [0, i].Runes[0];
+					text += Application.Driver.Contents [0, i].Runes [0];
 				}
 				return text;
 			}
@@ -1396,7 +1396,7 @@ Y
 
 			view1.Frame = new Rect (0, 0, 25, 4);
 			bool firstIteration = false;
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 
 			Assert.True (view1.AutoSize);
 			Assert.Equal (LayoutStyle.Absolute, view1.LayoutStyle);
@@ -1407,7 +1407,7 @@ Y
 			Assert.Equal ("Absolute(1)", view1.Height.ToString ());
 
 			view2.Frame = new Rect (0, 0, 1, 25);
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 
 			Assert.True (view2.AutoSize);
 			Assert.Equal (LayoutStyle.Absolute, view2.LayoutStyle);
@@ -1454,7 +1454,7 @@ Y
 			Assert.Equal ("Center", view2.Y.ToString ());
 			Assert.Equal ("Fill(0)", view2.Width.ToString ());
 			Assert.Equal ("Fill(0)", view2.Height.ToString ());
-		
+
 		}
 
 		[Fact, TestRespondersDisposed]
@@ -1598,7 +1598,7 @@ Y
 
 
 			((FakeDriver)Application.Driver).SetBufferSize (20, height);
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			var expected = string.Empty;
 
 			switch (height) {
@@ -1736,7 +1736,7 @@ Y
 
 
 			((FakeDriver)Application.Driver).SetBufferSize (width, 7);
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			var expected = string.Empty;
 
 			switch (width) {
@@ -1861,7 +1861,7 @@ Y
 			var clicked = false;
 			var top = Application.Top;
 			var win1 = new Window () { Id = "win1", Width = 20, Height = 10 };
-			var label= new Label ("[ ok ]");
+			var label = new Label ("[ ok ]");
 			var win2 = new Window () { Id = "win2", Y = Pos.Bottom (label) + 1, Width = 10, Height = 3 };
 			var view1 = new View () { Id = "view1", Width = Dim.Fill (), Height = 1, CanFocus = true };
 			view1.MouseClick += (sender, e) => clicked = true;
@@ -1893,14 +1893,11 @@ Y
 			Assert.Equal (new Rect (0, 0, 7, 1), view2.Frame);
 			var foundView = View.FindDeepestView (top, 9, 4, out int rx, out int ry);
 			Assert.Equal (foundView, view1);
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 9,
-					Y = 4,
-					Flags = MouseFlags.Button1Clicked
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 9,
+				Y = 4,
+				Flags = MouseFlags.Button1Clicked
+			}));
 			Assert.True (clicked);
 
 			Application.End (rs);
@@ -1920,7 +1917,7 @@ Y
 			};
 			top.Add (view);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				Assert.Equal (-2, view.Y);
 
 				Application.RequestStop ();
@@ -1947,7 +1944,7 @@ Y
 			var view = new View ("view") { X = -2 };
 			top.Add (view);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				Assert.Equal (-2, view.X);
 
 				Application.RequestStop ();

+ 7 - 7
UnitTests/View/Layout/PosTests.cs

@@ -540,7 +540,7 @@ namespace Terminal.Gui.ViewTests {
 			(Window win, Button button) setup ()
 			{
 				Application.Init (new FakeDriver ());
-				Application.Iteration = () => {
+				Application.Iteration += (s, a) => {
 					Application.RequestStop ();
 				};
 				var win = new Window () {
@@ -708,7 +708,7 @@ namespace Terminal.Gui.ViewTests {
 				Assert.Throws<ArgumentException> (() => v.Y = 2);
 			};
 
-			Application.Iteration += () => Application.RequestStop ();
+			Application.Iteration += (s, a) => Application.RequestStop ();
 
 			Application.Run ();
 			Application.Shutdown ();
@@ -729,7 +729,7 @@ namespace Terminal.Gui.ViewTests {
 				Assert.Equal (2, w.Y = 2);
 			};
 
-			Application.Iteration += () => Application.RequestStop ();
+			Application.Iteration += (s, a) => Application.RequestStop ();
 
 			Application.Run ();
 			Application.Shutdown ();
@@ -761,7 +761,7 @@ namespace Terminal.Gui.ViewTests {
 				Assert.Equal (2, v.Y = 2);
 			};
 
-			Application.Iteration += () => Application.RequestStop ();
+			Application.Iteration += (s, a) => Application.RequestStop ();
 
 			Application.Run ();
 			Application.Shutdown ();
@@ -809,7 +809,7 @@ namespace Terminal.Gui.ViewTests {
 		//		Assert.Equal (6, v2.Frame.Y);
 		//	};
 
-		//	Application.Iteration += () => Application.RequestStop ();
+		//	Application.Iteration += (s, a) => Application.RequestStop ();
 
 		//	Application.Run ();
 		//	Application.Shutdown ();
@@ -875,7 +875,7 @@ namespace Terminal.Gui.ViewTests {
 				}
 			};
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				while (count < 20) field.OnKeyDown (new KeyEvent (Key.Enter, new KeyModifiers ()));
 
 				Application.RequestStop ();
@@ -933,7 +933,7 @@ namespace Terminal.Gui.ViewTests {
 				}
 			};
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				while (count > 0) field.OnKeyDown (new KeyEvent (Key.Enter, new KeyModifiers ()));
 
 				Application.RequestStop ();

+ 11 - 11
UnitTests/View/NavigationTests.cs

@@ -515,7 +515,7 @@ namespace Terminal.Gui.ViewTests {
 			Assert.False (f.CanFocus);
 			Assert.True (v.CanFocus);
 
-			Application.Iteration += () => Application.RequestStop ();
+			Application.Iteration += (s, a) => Application.RequestStop ();
 
 			Application.Run ();
 			Application.Shutdown ();
@@ -558,7 +558,7 @@ namespace Terminal.Gui.ViewTests {
 				Assert.True (v.CanFocus);
 			};
 
-			Application.Iteration += () => Application.RequestStop ();
+			Application.Iteration += (s, a) => Application.RequestStop ();
 
 			Application.Run ();
 			Application.Shutdown ();
@@ -593,7 +593,7 @@ namespace Terminal.Gui.ViewTests {
 				Assert.False (v2.CanFocus);
 			};
 
-			Application.Iteration += () => Application.RequestStop ();
+			Application.Iteration += (s, a) => Application.RequestStop ();
 
 			Application.Run ();
 			Application.Shutdown ();
@@ -634,7 +634,7 @@ namespace Terminal.Gui.ViewTests {
 				Assert.True (v2.CanFocus);
 			};
 
-			Application.Iteration += () => Application.RequestStop ();
+			Application.Iteration += (s, a) => Application.RequestStop ();
 
 			Application.Run ();
 			Application.Shutdown ();
@@ -654,7 +654,7 @@ namespace Terminal.Gui.ViewTests {
 			// Keyboard navigation with tab
 			Console.MockKeyPresses.Push (new ConsoleKeyInfo ('\t', ConsoleKey.Tab, false, false, false));
 
-			Application.Iteration += () => Application.RequestStop ();
+			Application.Iteration += (s, a) => Application.RequestStop ();
 
 			Application.Run ();
 			Application.Shutdown ();
@@ -702,7 +702,7 @@ namespace Terminal.Gui.ViewTests {
 
 			var iterations = 0;
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 
 				button.ProcessKey (new KeyEvent (Key.Enter, null));
@@ -899,7 +899,7 @@ namespace Terminal.Gui.ViewTests {
 				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => sbQuiting = true )
 			});
 			var tf = new TextField ();
-			tf.KeyPress += Tf_KeyPress;
+			tf.KeyPressed += Tf_KeyPress;
 
 			void Tf_KeyPress (object sender, KeyEventEventArgs obj)
 			{
@@ -911,7 +911,7 @@ namespace Terminal.Gui.ViewTests {
 			var win = new Window ();
 			win.Add (sb, tf);
 			var top = Application.Top;
-			top.KeyPress += Top_KeyPress;
+			top.KeyPressed += Top_KeyPress;
 
 			void Top_KeyPress (object sender, KeyEventEventArgs obj)
 			{
@@ -932,7 +932,7 @@ namespace Terminal.Gui.ViewTests {
 			Assert.True (tfQuiting);
 			Assert.False (topQuiting);
 
-			tf.KeyPress -= Tf_KeyPress;
+			tf.KeyPressed -= Tf_KeyPress;
 			tfQuiting = false;
 			Application.Driver.SendKeys ('q', ConsoleKey.Q, false, false, true);
 			Application.MainLoop.RunIteration ();
@@ -959,7 +959,7 @@ namespace Terminal.Gui.ViewTests {
 				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => sbQuiting = true )
 			});
 			var tf = new TextField ();
-			tf.KeyPress += Tf_KeyPress;
+			tf.KeyPressed += Tf_KeyPress;
 
 			void Tf_KeyPress (object sender, KeyEventEventArgs obj)
 			{
@@ -981,7 +981,7 @@ namespace Terminal.Gui.ViewTests {
 			Assert.False (sbQuiting);
 			Assert.True (tfQuiting);
 
-			tf.KeyPress -= Tf_KeyPress;
+			tf.KeyPressed -= Tf_KeyPress;
 			tfQuiting = false;
 			Application.Driver.SendKeys ('q', ConsoleKey.Q, false, false, true);
 			Application.MainLoop.RunIteration ();

+ 4 - 4
UnitTests/View/ViewTests.cs

@@ -332,7 +332,7 @@ namespace Terminal.Gui.ViewTests {
 			w.Add (v1, v2);
 			t.Add (w);
 
-			Application.Iteration = () => {
+			Application.Iteration += (s, a) => {
 				Application.Refresh ();
 				t.Running = false;
 			};
@@ -403,7 +403,7 @@ namespace Terminal.Gui.ViewTests {
 			w.Add (v1, v2);
 			t.Add (w);
 
-			Application.Iteration = () => {
+			Application.Iteration += (s, a) => {
 				var sv1 = new View () { Id = "sv1", Width = Dim.Fill (), Height = Dim.Fill () };
 
 				sv1.Initialized += (s, e) => {
@@ -564,7 +564,7 @@ namespace Terminal.Gui.ViewTests {
 
 			var iterations = 0;
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 
 				Assert.True (button.Visible);
@@ -846,7 +846,7 @@ namespace Terminal.Gui.ViewTests {
 			label.Visible = false;
 
 			bool firstIteration = false;
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 ┌────────────────────────────┐
 │                            │

+ 32 - 47
UnitTests/Views/ContextMenuTests.cs

@@ -180,7 +180,7 @@ namespace Terminal.Gui.ViewsTests {
 
 			var cm = new ContextMenu ();
 
-			lbl.KeyPress += (s, e) => {
+			lbl.KeyPressed += (s, e) => {
 				if (e.KeyEvent.Key == cm.Key) {
 					lbl.Text = "Replaced";
 					e.Handled = true;
@@ -957,17 +957,14 @@ namespace Terminal.Gui.ViewsTests {
 │                  │
 └──────────────────┘", output);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 9,
-					Y = 3,
-					Flags = MouseFlags.Button3Clicked
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 9,
+				Y = 3,
+				Flags = MouseFlags.Button3Clicked
+			}));
 
 			var firstIteration = false;
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 ┌──────────────────┐
 │                  │
@@ -1008,17 +1005,14 @@ namespace Terminal.Gui.ViewsTests {
   │             │
   └─────────────┘", output);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 9,
-					Y = 3,
-					Flags = MouseFlags.Button3Clicked
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 9,
+				Y = 3,
+				Flags = MouseFlags.Button3Clicked
+			}));
 
 			var firstIteration = false;
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
   ┌─────────────┐   
   │ Test        │   
@@ -1052,17 +1046,14 @@ namespace Terminal.Gui.ViewsTests {
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
     Test", output);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 8,
-					Y = 2,
-					Flags = MouseFlags.Button3Clicked
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 8,
+				Y = 2,
+				Flags = MouseFlags.Button3Clicked
+			}));
 
 			var firstIteration = false;
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
     Test            
 ┌───────────────────
@@ -1103,17 +1094,14 @@ namespace Terminal.Gui.ViewsTests {
      │ Three  │
      └────────┘", output);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 5,
-					Y = 13,
-					Flags = MouseFlags.Button1Clicked
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 5,
+				Y = 13,
+				Flags = MouseFlags.Button1Clicked
+			}));
 
 			var firstIteration = false;
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			Assert.Equal (new Rect (5, 11, 10, 5), Application.Top.Subviews [0].Frame);
 			Assert.Equal (new Rect (5, 11, 15, 6), Application.Top.Subviews [1].Frame);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
@@ -1124,17 +1112,14 @@ namespace Terminal.Gui.ViewsTests {
      │ Sub-Menu 2  │
      └─────────────┘", output);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 5,
-					Y = 12,
-					Flags = MouseFlags.Button1Clicked
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 5,
+				Y = 12,
+				Flags = MouseFlags.Button1Clicked
+			}));
 
 			firstIteration = false;
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			Assert.Equal (new Rect (5, 11, 10, 5), Application.Top.Subviews [0].Frame);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
      ┌────────┐
@@ -1154,7 +1139,7 @@ namespace Terminal.Gui.ViewsTests {
 			var isMenuAllClosed = false;
 			MenuBarItem mi = null;
 			var iterations = -1;
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 				if (iterations == 0) {
 					cm.Show ();

+ 2 - 2
UnitTests/Views/GraphViewTests.cs

@@ -69,7 +69,7 @@ namespace Terminal.Gui.ViewsTests {
 				throw new Exception ("A test did not call shutdown correctly.  Test stack trace was:" + LastInitFakeDriver);
 			}
 
-			driver.Init (() => { });
+			driver.Init ();
 
 			LastInitFakeDriver = Environment.StackTrace;
 			return driver;
@@ -1516,7 +1516,7 @@ namespace Terminal.Gui.ViewsTests {
 		{
 			var driver = new FakeDriver ();
 			Application.Init (driver);
-			driver.Init (() => { });
+			driver.Init ();
 
 			// create a wide window
 			var mount = new View () {

+ 32 - 44
UnitTests/Views/MenuTests.cs

@@ -1677,7 +1677,7 @@ Edit
 			var top = Application.Top;
 			top.Add (win);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				((FakeDriver)Application.Driver).SetBufferSize (40, 8);
 
 				TestHelpers.AssertDriverContentsWithFrameAre (@"
@@ -1907,7 +1907,7 @@ Edit
 		{
 			((FakeDriver)Application.Driver).SetBufferSize (40, 8);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				var top = Application.Top;
 
 				TestHelpers.AssertDriverContentsWithFrameAre (@"
@@ -2376,7 +2376,7 @@ wo
 			Assert.Equal ("File", menu.Menus [0].Title);
 			menu.OpenMenu ();
 			var firstIteration = false;
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 ┌──────────────────────────────────────┐
 │                                      │
@@ -2394,17 +2394,14 @@ wo
 │                                      │
 └──────────────────────────────────────┘", output);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 20,
-					Y = 4,
-					Flags = MouseFlags.Button1Clicked
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 20,
+				Y = 4,
+				Flags = MouseFlags.Button1Clicked
+			}));
 
 			firstIteration = false;
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			Assert.Equal (items [0], menu.Menus [0].Title);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 ┌──────────────────────────────────────┐
@@ -2426,24 +2423,21 @@ wo
 			for (int i = 1; i < items.Count; i++) {
 				menu.OpenMenu ();
 
-				ReflectionTools.InvokePrivate (
-					typeof (Application),
-					"ProcessMouseEvent",
-					new MouseEvent () {
-						X = 20,
-						Y = 4 + i,
-						Flags = MouseFlags.Button1Clicked
-					});
+				Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+					X = 20,
+					Y = 4 + i,
+					Flags = MouseFlags.Button1Clicked
+				}));
 
 				firstIteration = false;
-				Application.RunMainLoopIteration (ref rs, ref firstIteration);
+				Application.RunIteration (ref rs, ref firstIteration);
 				Assert.Equal (items [i], menu.Menus [0].Title);
 			}
 
 			((FakeDriver)Application.Driver).SetBufferSize (20, 15);
 			menu.OpenMenu ();
 			firstIteration = false;
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 ┌──────────────────┐
 │                  │
@@ -2505,7 +2499,7 @@ wo
 			Assert.Equal ("File", menu.Menus [0].Title);
 			menu.OpenMenu ();
 			var firstIteration = false;
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
   ┌─────────────┐                       
   │  File       │                       
@@ -2518,17 +2512,14 @@ wo
     │ Delete     Delete a file  Ctrl+A │
     └──────────────────────────────────┘", output);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 20,
-					Y = 5,
-					Flags = MouseFlags.Button1Clicked
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 20,
+				Y = 5,
+				Flags = MouseFlags.Button1Clicked
+			}));
 
 			firstIteration = false;
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			Assert.Equal (items [0], menu.Menus [0].Title);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
   ┌─────────────┐
@@ -2539,24 +2530,21 @@ wo
 			for (int i = 1; i < items.Count; i++) {
 				menu.OpenMenu ();
 
-				ReflectionTools.InvokePrivate (
-					typeof (Application),
-					"ProcessMouseEvent",
-					new MouseEvent () {
-						X = 20,
-						Y = 5 + i,
-						Flags = MouseFlags.Button1Clicked
-					});
+				Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+					X = 20,
+					Y = 5 + i,
+					Flags = MouseFlags.Button1Clicked
+				}));
 
 				firstIteration = false;
-				Application.RunMainLoopIteration (ref rs, ref firstIteration);
+				Application.RunIteration (ref rs, ref firstIteration);
 				Assert.Equal (items [i], menu.Menus [0].Title);
 			}
 
 			((FakeDriver)Application.Driver).SetBufferSize (20, 15);
 			menu.OpenMenu ();
 			firstIteration = false;
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
   ┌─────────────┐   
   │  Delete     │   
@@ -2585,7 +2573,7 @@ wo
 
 			menu.OpenMenu ();
 			var firstIteration = false;
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
  File                         
 ┌────────────────────────────┐
@@ -2594,7 +2582,7 @@ wo
 
 			((FakeDriver)Application.Driver).SetBufferSize (20, 15);
 			firstIteration = false;
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
  File", output);
 

+ 10 - 10
UnitTests/Views/OverlappedTests.cs

@@ -88,7 +88,7 @@ namespace Terminal.Gui.ViewsTests {
 
 			d.Closed += (s, e) => Application.RequestStop (top1);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				Assert.Null (Application.OverlappedChildren);
 				if (iterations == 4) Assert.True (Application.Current == d);
 				else if (iterations == 3) Assert.True (Application.Current == top4);
@@ -154,7 +154,7 @@ namespace Terminal.Gui.ViewsTests {
 				overlapped.RequestStop ();
 			};
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				if (iterations == 4) {
 					// The Dialog was not closed before and will be closed now.
 					Assert.True (Application.Current == d);
@@ -212,7 +212,7 @@ namespace Terminal.Gui.ViewsTests {
 			// Now this will close the OverlappedContainer propagating through the OverlappedChildren.
 			d.Closed += (s, e) => Application.RequestStop (overlapped);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				if (iterations == 4) {
 					// The Dialog was not closed before and will be closed now.
 					Assert.True (Application.Current == d);
@@ -270,7 +270,7 @@ namespace Terminal.Gui.ViewsTests {
 			// Now this will close the OverlappedContainer propagating through the OverlappedChildren.
 			d.Closed += (s, e) => Application.RequestStop (overlapped);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				if (iterations == 4) {
 					// The Dialog still is the current top and we can't request stop to OverlappedContainer
 					// because we are not using parameter calls.
@@ -298,7 +298,7 @@ namespace Terminal.Gui.ViewsTests {
 			var c3 = new Window ();
 			var d = new Dialog ();
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				Assert.False (overlapped.IsOverlapped);
 				Assert.True (c1.IsOverlapped);
 				Assert.True (c2.IsOverlapped);
@@ -362,7 +362,7 @@ namespace Terminal.Gui.ViewsTests {
 				overlapped.RequestStop ();
 			};
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				if (iterations == 5) {
 					// The Dialog2 still is the current top and we can't request stop to OverlappedContainer
 					// because Dialog2 and Dialog1 must be closed first.
@@ -433,7 +433,7 @@ namespace Terminal.Gui.ViewsTests {
 				overlapped.RequestStop ();
 			};
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				if (iterations == 5) {
 					// The Dialog2 still is the current top and we can't request stop to OverlappedContainer
 					// because Dialog2 and Dialog1 must be closed first.
@@ -487,7 +487,7 @@ namespace Terminal.Gui.ViewsTests {
 			c1.Closed += (s, e) => {
 				overlapped.RequestStop ();
 			};
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				if (iterations == 3) {
 					// The Current still is c3 because Current.Running is false.
 					Assert.True (Application.Current == c3);
@@ -555,7 +555,7 @@ namespace Terminal.Gui.ViewsTests {
 
 			logger.Ready += (s, e) => Assert.Single (Application.OverlappedChildren);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				if (stageCompleted && running) {
 					stageCompleted = false;
 					var stage = new Window () { Modal = true };
@@ -635,7 +635,7 @@ namespace Terminal.Gui.ViewsTests {
 			overlapped.AllChildClosed += (s, e) => {
 				overlapped.RequestStop ();
 			};
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				if (iterations == 3) {
 					// The Current still is c3 because Current.Running is false.
 					Assert.True (Application.Current == c3);

+ 10 - 16
UnitTests/Views/ScrollBarViewTests.cs

@@ -1111,14 +1111,11 @@ This is a test
 This is a test             
 This is a test             ", output);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 15,
-					Y = 0,
-					Flags = MouseFlags.Button1Clicked
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 15,
+				Y = 0,
+				Flags = MouseFlags.Button1Clicked
+			}));
 
 			Assert.Null (Application.MouseGrabView);
 			Assert.True (clicked);
@@ -1138,14 +1135,11 @@ This is a test
 This is a test             
 This is a test             ", output);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 15,
-					Y = 0,
-					Flags = MouseFlags.Button1Clicked
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 15,
+				Y = 0,
+				Flags = MouseFlags.Button1Clicked
+			}));
 
 			Assert.Null (Application.MouseGrabView);
 			Assert.True (clicked);

+ 2 - 2
UnitTests/Views/StatusBarTests.cs

@@ -45,7 +45,7 @@ namespace Terminal.Gui.ViewsTests {
 			Assert.Equal (CursorVisibility.Default, cv);
 			Assert.True (FakeConsole.CursorVisible);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				Assert.Equal (24, sb.Frame.Y);
 
 				driver.SetWindowSize (driver.Cols, 15);
@@ -77,7 +77,7 @@ namespace Terminal.Gui.ViewsTests {
 
 			var iteration = 0;
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				if (iteration == 0) {
 					Assert.Equal ("", msg);
 					sb.ProcessHotKey (new KeyEvent (Key.CtrlMask | Key.Q, null));

+ 1 - 1
UnitTests/Views/TabViewTests.cs

@@ -850,7 +850,7 @@ namespace Terminal.Gui.ViewsTests {
 		{
 			var driver = new FakeDriver ();
 			Application.Init (driver);
-			driver.Init (() => { });
+			driver.Init ();
 		}
 	}
 }

+ 13 - 17
UnitTests/Views/TextFieldTests.cs

@@ -1220,7 +1220,7 @@ namespace Terminal.Gui.ViewsTests {
 		[AutoInitShutdown]
 		public void Test_RootKeyEvent_Cancel ()
 		{
-			Application.RootKeyEvent += SuppressKey;
+			Application.KeyPressed += SuppressKey;
 
 			var tf = new TextField ();
 
@@ -1234,7 +1234,7 @@ namespace Terminal.Gui.ViewsTests {
 			Application.Driver.SendKeys ('j', ConsoleKey.A, false, false, false);
 			Assert.Equal ("a", tf.Text);
 
-			Application.RootKeyEvent -= SuppressKey;
+			Application.KeyPressed -= SuppressKey;
 
 			// Now that the delegate has been removed we can type j again
 			Application.Driver.SendKeys ('j', ConsoleKey.A, false, false, false);
@@ -1244,7 +1244,7 @@ namespace Terminal.Gui.ViewsTests {
 		[AutoInitShutdown]
 		public void Test_RootMouseKeyEvent_Cancel ()
 		{
-			Application.RootMouseEvent += SuppressRightClick;
+			Application.MouseEvent += SuppressRightClick;
 
 			var tf = new TextField () { Width = 10 };
 			int clickCounter = 0;
@@ -1253,15 +1253,12 @@ namespace Terminal.Gui.ViewsTests {
 			Application.Top.Add (tf);
 			Application.Begin (Application.Top);
 
-			var processMouseEventMethod = typeof (Application).GetMethod ("ProcessMouseEvent", BindingFlags.Static | BindingFlags.NonPublic)
-			    ?? throw new Exception ("Expected private method not found 'ProcessMouseEvent', this method was used for testing mouse behaviours");
-
 			var mouseEvent = new MouseEvent {
 				Flags = MouseFlags.Button1Clicked,
 				View = tf
 			};
 
-			processMouseEventMethod.Invoke (null, new object [] { mouseEvent });
+			Application.OnMouseEvent(new MouseEventEventArgs(mouseEvent));
 			Assert.Equal (1, clickCounter);
 
 			// Get a fresh instance that represents a right click.
@@ -1270,10 +1267,10 @@ namespace Terminal.Gui.ViewsTests {
 				Flags = MouseFlags.Button3Clicked,
 				View = tf
 			};
-			processMouseEventMethod.Invoke (null, new object [] { mouseEvent });
+			Application.OnMouseEvent (new MouseEventEventArgs (mouseEvent));
 			Assert.Equal (1, clickCounter);
 
-			Application.RootMouseEvent -= SuppressRightClick;
+			Application.MouseEvent -= SuppressRightClick;
 
 			// Get a fresh instance that represents a right click.
 			// Should no longer be ignored as the callback was removed
@@ -1282,21 +1279,20 @@ namespace Terminal.Gui.ViewsTests {
 				View = tf
 			};
 
-			processMouseEventMethod.Invoke (null, new object [] { mouseEvent });
+			Application.OnMouseEvent (new MouseEventEventArgs (mouseEvent));
 			Assert.Equal (2, clickCounter);
 		}
 
-		private bool SuppressKey (KeyEvent arg)
+		private void SuppressKey (object s, KeyEventEventArgs arg)
 		{
-			if (arg.KeyValue == 'j')
-				return true;
-
-			return false;
+			if (arg.KeyEvent.KeyValue == 'j') {
+				arg.Handled = true;
+			}
 		}
 
-		private void SuppressRightClick (MouseEvent arg)
+		private void SuppressRightClick (object sender, MouseEventEventArgs arg)
 		{
-			if (arg.Flags.HasFlag (MouseFlags.Button3Clicked))
+			if (arg.MouseEvent.Flags.HasFlag (MouseFlags.Button3Clicked))
 				arg.Handled = true;
 		}
 

+ 9 - 9
UnitTests/Views/TextViewTests.cs

@@ -1604,7 +1604,7 @@ namespace Terminal.Gui.ViewsTests {
 		{
 			Application.Top.Add (_textView);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				var width = _textView.Bounds.Width - 1;
 				Assert.Equal (30, width + 1);
 				Assert.Equal (10, _textView.Height);
@@ -1640,7 +1640,7 @@ namespace Terminal.Gui.ViewsTests {
 		{
 			Application.Top.Add (_textView);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				var width = _textView.Bounds.Width - 1;
 				Assert.Equal (30, width + 1);
 				Assert.Equal (10, _textView.Height);
@@ -1683,7 +1683,7 @@ namespace Terminal.Gui.ViewsTests {
 		{
 			Application.Top.Add (_textView);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				var width = _textView.Bounds.Width - 1;
 				Assert.Equal (30, width + 1);
 				Assert.Equal (10, _textView.Height);
@@ -1726,7 +1726,7 @@ namespace Terminal.Gui.ViewsTests {
 		{
 			Application.Top.Add (_textView);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				var width = _textView.Bounds.Width - 1;
 				Assert.Equal (30, width + 1);
 				Assert.Equal (10, _textView.Height);
@@ -1762,7 +1762,7 @@ namespace Terminal.Gui.ViewsTests {
 		{
 			Application.Top.Add (_textView);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				var width = _textView.Bounds.Width - 1;
 				Assert.Equal (30, width + 1);
 				Assert.Equal (10, _textView.Height);
@@ -1820,7 +1820,7 @@ namespace Terminal.Gui.ViewsTests {
 		{
 			Application.Top.Add (_textView);
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				var width = _textView.Bounds.Width - 1;
 				Assert.Equal (30, width + 1);
 				Assert.Equal (10, _textView.Height);
@@ -6618,7 +6618,7 @@ This is the second line.
 		[Fact, AutoInitShutdown]
 		public void ContentsChanged_Event_Fires_On_Init ()
 		{
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				Application.RequestStop ();
 			};
 
@@ -6644,7 +6644,7 @@ This is the second line.
 		[Fact, AutoInitShutdown]
 		public void ContentsChanged_Event_Fires_On_Set_Text ()
 		{
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				Application.RequestStop ();
 			};
 			var eventcount = 0;
@@ -6679,7 +6679,7 @@ This is the second line.
 		[Fact, AutoInitShutdown]
 		public void ContentsChanged_Event_Fires_On_Typing ()
 		{
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				Application.RequestStop ();
 			};
 			var eventcount = 0;

+ 119 - 185
UnitTests/Views/ToplevelTests.cs

@@ -45,7 +45,7 @@ namespace Terminal.Gui.ViewsTests {
 		{
 			var iterations = 0;
 
-			Application.Iteration += () => {
+			Application.Iteration += (s, a) => {
 				if (iterations == 0) {
 					Assert.False (Application.Top.AutoSize);
 					Assert.Equal ("Top1", Application.Top.Text);
@@ -674,7 +674,7 @@ namespace Terminal.Gui.ViewsTests {
 			top.Add (win);
 			var iterations = -1;
 
-			Application.Iteration = () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 				if (iterations == 0) {
 					((FakeDriver)Application.Driver).SetBufferSize (40, 15);
@@ -701,14 +701,11 @@ namespace Terminal.Gui.ViewsTests {
 				} else if (iterations == 2) {
 					Assert.Null (Application.MouseGrabView);
 					// Grab the mouse
-					ReflectionTools.InvokePrivate (
-						typeof (Application),
-						"ProcessMouseEvent",
-						new MouseEvent () {
-							X = 8,
-							Y = 5,
-							Flags = MouseFlags.Button1Pressed
-						});
+					Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+						X = 8,
+						Y = 5,
+						Flags = MouseFlags.Button1Pressed
+					}));
 
 					Assert.Equal (Application.Current, Application.MouseGrabView);
 					Assert.Equal (new Rect (8, 5, 24, 5), Application.MouseGrabView.Frame);
@@ -716,14 +713,11 @@ namespace Terminal.Gui.ViewsTests {
 				} else if (iterations == 3) {
 					Assert.Equal (Application.Current, Application.MouseGrabView);
 					// Drag to left
-					ReflectionTools.InvokePrivate (
-						typeof (Application),
-						"ProcessMouseEvent",
-						new MouseEvent () {
-							X = 7,
-							Y = 5,
-							Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
-						});
+					Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+						X = 7,
+						Y = 5,
+						Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+					}));
 
 					Assert.Equal (Application.Current, Application.MouseGrabView);
 					Assert.Equal (new Rect (7, 5, 24, 5), Application.MouseGrabView.Frame);
@@ -752,14 +746,11 @@ namespace Terminal.Gui.ViewsTests {
 				} else if (iterations == 5) {
 					Assert.Equal (Application.Current, Application.MouseGrabView);
 					// Drag up
-					ReflectionTools.InvokePrivate (
-						typeof (Application),
-						"ProcessMouseEvent",
-						new MouseEvent () {
-							X = 7,
-							Y = 4,
-							Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
-						});
+					Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+						X = 7,
+						Y = 4,
+						Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+					}));
 
 					Assert.Equal (Application.Current, Application.MouseGrabView);
 					Assert.Equal (new Rect (7, 4, 24, 5), Application.MouseGrabView.Frame);
@@ -790,14 +781,11 @@ namespace Terminal.Gui.ViewsTests {
 				} else if (iterations == 7) {
 					Assert.Equal (Application.Current, Application.MouseGrabView);
 					// Ungrab the mouse
-					ReflectionTools.InvokePrivate (
-						typeof (Application),
-						"ProcessMouseEvent",
-						new MouseEvent () {
-							X = 7,
-							Y = 4,
-							Flags = MouseFlags.Button1Released
-						});
+					Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+						X = 7,
+						Y = 4,
+						Flags = MouseFlags.Button1Released
+					}));
 
 					Assert.Null (Application.MouseGrabView);
 
@@ -827,7 +815,7 @@ namespace Terminal.Gui.ViewsTests {
 
 			var location = new Rect (win.Frame.X, win.Frame.Y, 7, 3);
 
-			Application.Iteration = () => {
+			Application.Iteration += (s, a) => {
 				iterations++;
 				if (iterations == 0) {
 					((FakeDriver)Application.Driver).SetBufferSize (30, 10);
@@ -836,14 +824,11 @@ namespace Terminal.Gui.ViewsTests {
 
 					Assert.Null (Application.MouseGrabView);
 					// Grab the mouse
-					ReflectionTools.InvokePrivate (
-						typeof (Application),
-						"ProcessMouseEvent",
-						new MouseEvent () {
-							X = win.Frame.X,
-							Y = win.Frame.Y,
-							Flags = MouseFlags.Button1Pressed
-						});
+					Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+						X = win.Frame.X,
+						Y = win.Frame.Y,
+						Flags = MouseFlags.Button1Pressed
+					}));
 
 					Assert.Equal (win, Application.MouseGrabView);
 					Assert.Equal (location, Application.MouseGrabView.Frame);
@@ -852,14 +837,11 @@ namespace Terminal.Gui.ViewsTests {
 					// Drag to left
 					movex = 1;
 					movey = 0;
-					ReflectionTools.InvokePrivate (
-						typeof (Application),
-						"ProcessMouseEvent",
-						new MouseEvent () {
-							X = win.Frame.X + movex,
-							Y = win.Frame.Y + movey,
-							Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
-						});
+					Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+						X = win.Frame.X + movex,
+						Y = win.Frame.Y + movey,
+						Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+					}));
 
 					Assert.Equal (win, Application.MouseGrabView);
 
@@ -875,14 +857,11 @@ namespace Terminal.Gui.ViewsTests {
 					// Drag up
 					movex = 0;
 					movey = -1;
-					ReflectionTools.InvokePrivate (
-						typeof (Application),
-						"ProcessMouseEvent",
-						new MouseEvent () {
-							X = win.Frame.X + movex,
-							Y = win.Frame.Y + movey,
-							Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
-						});
+					Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+						X = win.Frame.X + movex,
+						Y = win.Frame.Y + movey,
+						Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+					}));
 
 					Assert.Equal (win, Application.MouseGrabView);
 
@@ -897,14 +876,11 @@ namespace Terminal.Gui.ViewsTests {
 					// Ungrab the mouse
 					movex = 0;
 					movey = 0;
-					ReflectionTools.InvokePrivate (
-						typeof (Application),
-						"ProcessMouseEvent",
-						new MouseEvent () {
-							X = win.Frame.X + movex,
-							Y = win.Frame.Y + movey,
-							Flags = MouseFlags.Button1Released
-						});
+					Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+						X = win.Frame.X + movex,
+						Y = win.Frame.Y + movey,
+						Flags = MouseFlags.Button1Released
+					}));
 
 					Assert.Null (Application.MouseGrabView);
 				} else if (iterations == 7) {
@@ -1162,25 +1138,19 @@ namespace Terminal.Gui.ViewsTests {
       │                                   ▼
    ◄├──────┤░░░░░░░░░░░░░░░░░░░░░░░░░░░░░► ", output);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 6,
-					Y = 6,
-					Flags = MouseFlags.Button1Pressed
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 6,
+				Y = 6,
+				Flags = MouseFlags.Button1Pressed
+			}));
 			Assert.Equal (win, Application.MouseGrabView);
 			Assert.Equal (new Rect (3, 3, 194, 94), win.Frame);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 9,
-					Y = 9,
-					Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 9,
+				Y = 9,
+				Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+			}));
 			Assert.Equal (win, Application.MouseGrabView);
 			top.SetNeedsLayout ();
 			top.LayoutSubviews ();
@@ -1204,14 +1174,11 @@ namespace Terminal.Gui.ViewsTests {
          │                                ▼
    ◄├──────┤░░░░░░░░░░░░░░░░░░░░░░░░░░░░░► ", output);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 5,
-					Y = 5,
-					Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 5,
+				Y = 5,
+				Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+			}));
 			Assert.Equal (win, Application.MouseGrabView);
 			top.SetNeedsLayout ();
 			top.LayoutSubviews ();
@@ -1235,24 +1202,18 @@ namespace Terminal.Gui.ViewsTests {
      │                                    ▼
    ◄├──────┤░░░░░░░░░░░░░░░░░░░░░░░░░░░░░► ", output);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 5,
-					Y = 5,
-					Flags = MouseFlags.Button1Released
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 5,
+				Y = 5,
+				Flags = MouseFlags.Button1Released
+			}));
 			Assert.Null (Application.MouseGrabView);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 4,
-					Y = 4,
-					Flags = MouseFlags.ReportMousePosition
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 4,
+				Y = 4,
+				Flags = MouseFlags.ReportMousePosition
+			}));
 			Assert.Equal (scrollView, Application.MouseGrabView);
 		}
 
@@ -1275,25 +1236,19 @@ namespace Terminal.Gui.ViewsTests {
 
 			Assert.Null (Application.MouseGrabView);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 10,
-					Y = 3,
-					Flags = MouseFlags.Button1Pressed
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 10,
+				Y = 3,
+				Flags = MouseFlags.Button1Pressed
+			}));
 
 			Assert.Equal (dialog, Application.MouseGrabView);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = -11,
-					Y = -4,
-					Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = -11,
+				Y = -4,
+				Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+			}));
 
 			Application.Refresh ();
 			Assert.Equal (new Rect (0, 0, 40, 10), top.Frame);
@@ -1306,14 +1261,11 @@ namespace Terminal.Gui.ViewsTests {
 
 			// Changes Top size to same size as Dialog more menu and scroll bar
 			((FakeDriver)Application.Driver).SetBufferSize (20, 3);
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = -1,
-					Y = -1,
-					Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = -1,
+				Y = -1,
+				Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+			}));
 
 			Application.Refresh ();
 			Assert.Equal (new Rect (0, 0, 20, 3), top.Frame);
@@ -1326,14 +1278,11 @@ namespace Terminal.Gui.ViewsTests {
 
 			// Changes Top size smaller than Dialog size
 			((FakeDriver)Application.Driver).SetBufferSize (19, 2);
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = -1,
-					Y = -1,
-					Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = -1,
+				Y = -1,
+				Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+			}));
 
 			Application.Refresh ();
 			Assert.Equal (new Rect (0, 0, 19, 2), top.Frame);
@@ -1343,14 +1292,11 @@ namespace Terminal.Gui.ViewsTests {
       {CM.Glyphs.LeftBracket} Ok {CM.Glyphs.RightBracket}      │
 ", output);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 18,
-					Y = 1,
-					Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 18,
+				Y = 1,
+				Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+			}));
 
 			Application.Refresh ();
 			Assert.Equal (new Rect (0, 0, 19, 2), top.Frame);
@@ -1359,14 +1305,11 @@ namespace Terminal.Gui.ViewsTests {
                   ┌", output);
 
 			// On a real app we can't go beyond the SuperView bounds
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 19,
-					Y = 2,
-					Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 19,
+				Y = 2,
+				Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+			}));
 
 			Application.Refresh ();
 			Assert.Equal (new Rect (0, 0, 19, 2), top.Frame);
@@ -1406,17 +1349,14 @@ namespace Terminal.Gui.ViewsTests {
                          │                            │
                          └────────────────────────────┘", output);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 25,
-					Y = 7,
-					Flags = MouseFlags.Button1Pressed
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 25,
+				Y = 7,
+				Flags = MouseFlags.Button1Pressed
+			}));
 
 			var firstIteration = false;
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			Assert.Equal (dialog, Application.MouseGrabView);
 
 			Assert.Equal (new Rect (25, 7, 30, 10), dialog.Frame);
@@ -1432,17 +1372,14 @@ namespace Terminal.Gui.ViewsTests {
                          │                            │
                          └────────────────────────────┘", output);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 20,
-					Y = 10,
-					Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 20,
+				Y = 10,
+				Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+			}));
 
 			firstIteration = false;
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			Assert.Equal (dialog, Application.MouseGrabView);
 			Assert.Equal (new Rect (20, 10, 30, 10), dialog.Frame);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
@@ -1550,17 +1487,14 @@ namespace Terminal.Gui.ViewsTests {
 │                  │
 └──────────────────┘", output);
 
-			ReflectionTools.InvokePrivate (
-				typeof (Application),
-				"ProcessMouseEvent",
-				new MouseEvent () {
-					X = 9,
-					Y = 13,
-					Flags = MouseFlags.Button1Clicked
-				});
+			Application.OnMouseEvent (new MouseEventEventArgs (new MouseEvent () {
+				X = 9,
+				Y = 13,
+				Flags = MouseFlags.Button1Clicked
+			}));
 
 			var firstIteration = false;
-			Application.RunMainLoopIteration (ref rs, ref firstIteration);
+			Application.RunIteration (ref rs, ref firstIteration);
 			TestHelpers.AssertDriverContentsWithFrameAre (@$"
 ┌──────────────────┐
 │                  │
@@ -1619,7 +1553,7 @@ namespace Terminal.Gui.ViewsTests {
 			bool fromTopStillKnowSecondIsRunning = false;
 			bool fromFirstStillKnowSecondIsRunning = false;
 
-			Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (100), (_) => {
+			Application.AddTimeout (TimeSpan.FromMilliseconds (100), () => {
 				count++;
 				if (count1 == 5) {
 					log1 = true;
@@ -1656,7 +1590,7 @@ namespace Terminal.Gui.ViewsTests {
 				var od = new OpenDialog ();
 				od.Ready += SecondDialogToplevel;
 
-				Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (100), (_) => {
+				Application.AddTimeout (TimeSpan.FromMilliseconds (100), () => {
 					count1++;
 					if (count2 == 5) {
 						log2 = true;
@@ -1679,7 +1613,7 @@ namespace Terminal.Gui.ViewsTests {
 			{
 				var d = new Dialog ();
 
-				Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (100), (_) => {
+				Application.AddTimeout (TimeSpan.FromMilliseconds (100), () => {
 					count2++;
 					if (count < 30) {
 						log = true;

+ 1 - 1
UnitTests/Views/TreeViewTests.cs

@@ -1280,7 +1280,7 @@ FFFFFFFFFF
 		{
 			var driver = new FakeDriver ();
 			Application.Init (driver);
-			driver.Init (() => { });
+			driver.Init ();
 		}
 	}
 }