Browse Source

Merge pull request #804 from BDisp/application-init-shutdown

Fixes #803. Init and Shutdown only run once.
Charlie Kindel 5 years ago
parent
commit
fb702e8432

+ 2 - 2
Example/demo.cs

@@ -274,7 +274,7 @@ static class Demo {
 			text.Text = System.IO.File.ReadAllText (fname);
 		win.Add (text);
 
-		Application.Run (ntop, false);
+		Application.Run (ntop);
 	}
 
 	static bool Quit ()
@@ -680,7 +680,7 @@ static class Demo {
 		top.Add (win);
 		//top.Add (menu);
 		top.Add (menu, statusBar);
-		Application.Run (top, false);
+		Application.Run (top);
 	}
 
 	private static void Win_KeyPress (View.KeyEventEventArgs e)

+ 7 - 6
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -420,7 +420,7 @@ namespace Terminal.Gui {
 				return v;
 			}
 		}
-		
+
 		public InputRecord [] ReadConsoleInput ()
 		{
 			const int bufferSize = 1;
@@ -438,7 +438,7 @@ namespace Terminal.Gui {
 				Marshal.FreeHGlobal (pRecord);
 			}
 		}
-
+#if false	// Not needed on the constructor. Perhaps could be used on resizing. To study.
 		[DllImport ("kernel32.dll", ExactSpelling = true)]
 		private static extern IntPtr GetConsoleWindow ();
 
@@ -455,7 +455,7 @@ namespace Terminal.Gui {
 			IntPtr thisConsole = GetConsoleWindow ();
 			ShowWindow (thisConsole, state);
 		}
-
+#endif
 #if false // See: https://github.com/migueldeicaza/gui.cs/issues/357
 		[StructLayout (LayoutKind.Sequential)]
 		public struct SMALL_RECT {
@@ -519,8 +519,9 @@ namespace Terminal.Gui {
 
 			cols = Console.WindowWidth;
 			rows = Console.WindowHeight;
+#if false
 			winConsole.ShowWindow (WindowsConsole.RESTORE);
-
+#endif
 			WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
 
 			ResizeScreen ();
@@ -1323,7 +1324,7 @@ namespace Terminal.Gui {
 			winConsole.Cleanup ();
 		}
 
-		#region Unused
+#region Unused
 		public override void SetColors (ConsoleColor foreground, ConsoleColor background)
 		{
 		}
@@ -1351,7 +1352,7 @@ namespace Terminal.Gui {
 		public override void CookMouse ()
 		{
 		}
-		#endregion
+#endregion
 
 	}
 

+ 28 - 46
Terminal.Gui/Core/Application.cs

@@ -161,15 +161,12 @@ namespace Terminal.Gui {
 
 		internal static bool _initialized = false;
 
-		static IMainLoopDriver oldMainLoopDriver;
-		static ConsoleDriver oldDriver;
-
 		/// <summary>
 		/// Initializes the Terminal.Gui application
 		/// </summary>
 		static void Init (Func<Toplevel> topLevelFactory, ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null)
 		{
-			if (_initialized) return;
+			if (_initialized && driver == null) return;
 
 			// This supports Unit Tests and the passing of a mock driver/loopdriver
 			if (driver != null) {
@@ -192,15 +189,8 @@ namespace Terminal.Gui {
 					mainLoopDriver = windowsDriver;
 					Driver = windowsDriver;
 				} else {
-					if (oldMainLoopDriver == null && oldDriver == null) {
-						mainLoopDriver = new UnixMainLoop ();
-						Driver = new CursesDriver ();
-						oldMainLoopDriver = mainLoopDriver;
-						oldDriver = Driver;
-					} else {
-						mainLoopDriver = oldMainLoopDriver;
-						Driver = oldDriver;
-					}
+					mainLoopDriver = new UnixMainLoop ();
+					Driver = new CursesDriver ();
 				}
 				Driver.Init (TerminalResized);
 				MainLoop = new MainLoop (mainLoopDriver);
@@ -216,8 +206,6 @@ namespace Terminal.Gui {
 		/// Captures the execution state for the provided <see cref="Toplevel"/>  view.
 		/// </summary>
 		public class RunState : IDisposable {
-			internal bool closeDriver = true;
-
 			/// <summary>
 			/// Initializes a new <see cref="RunState"/> class.
 			/// </summary>
@@ -238,7 +226,7 @@ namespace Terminal.Gui {
 			/// <see cref="Application.RunState"/> was occupying.</remarks>
 			public void Dispose ()
 			{
-				Dispose (closeDriver);
+				Dispose (true);
 				GC.SuppressFinalize (this);
 			}
 
@@ -249,8 +237,8 @@ namespace Terminal.Gui {
 			/// <param name="disposing">If set to <c>true</c> disposing.</param>
 			protected virtual void Dispose (bool disposing)
 			{
-				if (Toplevel != null) {
-					End (Toplevel, disposing);
+				if (Toplevel != null && disposing) {
+					End (Toplevel);
 					Toplevel.Dispose ();
 					Toplevel = null;
 				}
@@ -445,14 +433,14 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Building block API: Prepares the provided <see cref="Toplevel"/>  for execution.
 		/// </summary>
-		/// <returns>The runstate handle that needs to be passed to the <see cref="End(RunState, bool)"/> method upon completion.</returns>
+		/// <returns>The runstate handle that needs to be passed to the <see cref="End(RunState)"/> method upon completion.</returns>
 		/// <param name="toplevel">Toplevel to prepare execution for.</param>
 		/// <remarks>
 		///  This method prepares the provided toplevel for running with the focus,
 		///  it adds this to the list of toplevels, sets up the mainloop to process the
 		///  event, lays out the subviews, focuses the first element, and draws the
 		///  toplevel in the screen. This is usually followed by executing
-		///  the <see cref="RunLoop"/> method, and then the <see cref="End(RunState, bool)"/> method upon termination which will
+		///  the <see cref="RunLoop"/> method, and then the <see cref="End(RunState)"/> method upon termination which will
 		///   undo these changes.
 		/// </remarks>
 		public static RunState Begin (Toplevel toplevel)
@@ -489,21 +477,18 @@ namespace Terminal.Gui {
 		/// Building block API: completes the execution of a <see cref="Toplevel"/>  that was started with <see cref="Begin(Toplevel)"/> .
 		/// </summary>
 		/// <param name="runState">The runstate returned by the <see cref="Begin(Toplevel)"/> method.</param>
-		/// <param name="closeDriver">If <c>true</c>, closes the application. If <c>false</c> closes the toplevels only.</param>
-		public static void End (RunState runState, bool closeDriver = true)
+		public static void End (RunState runState)
 		{
 			if (runState == null)
 				throw new ArgumentNullException (nameof (runState));
 
-			runState.closeDriver = closeDriver;
 			runState.Dispose ();
 		}
 
 		/// <summary>
 		/// Shutdown an application initialized with <see cref="Init(ConsoleDriver, IMainLoopDriver)"/>
 		/// </summary>
-		/// <param name="closeDriver"><c>true</c>Closes the application.<c>false</c>Closes toplevels only.</param>
-		public static void Shutdown (bool closeDriver = true)
+		public static void Shutdown ()
 		{
 			// 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.
@@ -518,14 +503,9 @@ namespace Terminal.Gui {
 			CurrentView = null;
 			Top = null;
 
-			// Closes the application if it's true.
-			if (closeDriver) {
-				MainLoop = null;
-				Driver?.End ();
-				Driver = null;
-			}
-
-			_initialized = false;
+			MainLoop = null;
+			Driver?.End ();
+			Driver = null;
 		}
 
 		static void Redraw (View view)
@@ -558,15 +538,18 @@ namespace Terminal.Gui {
 			Driver.Refresh ();
 		}
 
-		internal static void End (View view, bool closeDriver = true)
+		internal static void End (View view)
 		{
 			if (toplevels.Peek () != view)
 				throw new ArgumentException ("The view that you end with must be balanced");
 			toplevels.Pop ();
-			if (toplevels.Count == 0)
-				Shutdown (closeDriver);
-			else {
+
+			if (toplevels.Count == 0) {
+				Current = null;
+				CurrentView = null;
+			} else {
 				Current = toplevels.Peek ();
+				CurrentView = Current;
 				Refresh ();
 			}
 		}
@@ -598,7 +581,7 @@ namespace Terminal.Gui {
 
 					MainLoop.MainIteration ();
 					Iteration?.Invoke ();
-				} else if (wait == false) {
+				} else if (!wait) {
 					return;
 				}
 				if (state.Toplevel != Top && (!Top.NeedDisplay.IsEmpty || Top.childNeedsDisplay)) {
@@ -630,7 +613,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Runs the application by calling <see cref="Run(Toplevel, bool)"/> with the value of <see cref="Top"/>
+		/// Runs the application by calling <see cref="Run(Toplevel)"/> with the value of <see cref="Top"/>
 		/// </summary>
 		public static void Run ()
 		{
@@ -638,7 +621,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Runs the application by calling <see cref="Run(Toplevel, bool)"/> with a new instance of the specified <see cref="Toplevel"/>-derived class
+		/// Runs the application by calling <see cref="Run(Toplevel)"/> with a new instance of the specified <see cref="Toplevel"/>-derived class
 		/// </summary>
 		public static void Run<T> () where T : Toplevel, new()
 		{
@@ -656,11 +639,11 @@ namespace Terminal.Gui {
 		///     run other modal <see cref="View"/>s such as <see cref="Dialog"/> boxes.
 		///   </para>
 		///   <para>
-		///     To make a <see cref="Run(Toplevel, bool)"/> stop execution, call <see cref="Application.RequestStop"/>.
+		///     To make a <see cref="Run(Toplevel)"/> stop execution, call <see cref="Application.RequestStop"/>.
 		///   </para>
 		///   <para>
-		///     Calling <see cref="Run(Toplevel, bool)"/> is equivalent to calling <see cref="Begin(Toplevel)"/>, followed by <see cref="RunLoop(RunState, bool)"/>,
-		///     and then calling <see cref="End(RunState, bool)"/>.
+		///     Calling <see cref="Run(Toplevel)"/> is equivalent to calling <see cref="Begin(Toplevel)"/>, followed by <see cref="RunLoop(RunState, bool)"/>,
+		///     and then calling <see cref="End(RunState)"/>.
 		///   </para>
 		///   <para>
 		///     Alternatively, to have a program control the main loop and 
@@ -671,12 +654,11 @@ namespace Terminal.Gui {
 		///   </para>
 		/// </remarks>
 		/// <param name="view">The <see cref="Toplevel"/> tu run modally.</param>
-		/// <param name="closeDriver">Set to <true/> to cause the MainLoop to end when <see cref="End(RunState, bool)"/> is called, clsing the toplevels only.</param>
-		public static void Run (Toplevel view, bool closeDriver = true)
+		public static void Run (Toplevel view)
 		{
 			var runToken = Begin (view);
 			RunLoop (runToken);
-			End (runToken, closeDriver);
+			End (runToken);
 		}
 
 		/// <summary>

+ 3 - 3
Terminal.Gui/Core/Toplevel.cs

@@ -15,7 +15,7 @@ namespace Terminal.Gui {
 	/// </summary>
 	/// <remarks>
 	///   <para>
-	///     Toplevels can be modally executing views, started by calling <see cref="Application.Run(Toplevel, bool)"/>. 
+	///     Toplevels can be modally executing views, started by calling <see cref="Application.Run(Toplevel)"/>. 
 	///     They return control to the caller when <see cref="Application.RequestStop()"/> has 
 	///     been called (which sets the <see cref="Toplevel.Running"/> property to false). 
 	///   </para>
@@ -23,7 +23,7 @@ namespace Terminal.Gui {
 	///     A Toplevel is created when an application initialzies Terminal.Gui by callling <see cref="Application.Init(ConsoleDriver, IMainLoopDriver)"/>.
 	///     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, bool)"/>.
+	///     call <see cref="Application.Run(Toplevel)"/>.
 	///   </para>
 	///   <para>
 	///     Toplevels can also opt-in to more sophisticated initialization
@@ -382,7 +382,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Invoked by <see cref="Application.Begin"/> as part of the <see cref="Application.Run(Toplevel, bool)"/> after
+		/// Invoked by <see cref="Application.Begin"/> as part of the <see cref="Application.Run(Toplevel)"/> after
 		/// the views have been laid out, and before the views are drawn for the first time.
 		/// </summary>
 		public virtual void WillPresent ()

+ 1 - 1
Terminal.Gui/Windows/MessageBox.cs

@@ -149,7 +149,7 @@ namespace Terminal.Gui {
 			}
 
 			// Rin the modal; do not shutdown the mainloop driver when done
-			Application.Run (d, false);
+			Application.Run (d);
 			return clicked;
 		}
 	}

+ 2 - 2
UICatalog/Scenario.cs

@@ -197,8 +197,8 @@ namespace UICatalog {
 		/// </remarks>
 		public virtual void Run ()
 		{
-			// This method already performs a later automatic shutdown.
-			Application.Run (Top, false);
+			// Must explicit call Application.Shutdown method to shutdown.
+			Application.Run (Top);
 		}
 
 		/// <summary>

+ 1 - 1
UICatalog/Scenarios/HexEditor.cs

@@ -102,7 +102,7 @@ namespace UICatalog {
 		private void Open ()
 		{
 			var d = new OpenDialog ("Open", "Open a file") { AllowsMultipleSelection = false };
-			Application.Run (d, false);
+			Application.Run (d);
 
 			if (!d.Canceled) {
 				_fileName = d.FilePaths [0];

+ 6 - 3
UICatalog/UICatalog.cs

@@ -93,10 +93,13 @@ namespace UICatalog {
 #endif
 
 				Application.UseSystemConsole = _useSystemConsole;
-				Application.Init ();
 				scenario.Init (Application.Top, _baseColorScheme);
 				scenario.Setup ();
 				scenario.Run ();
+				_top.Ready += () => {
+					_top.SetFocus (_rightPane);
+					_top.Ready = null;
+				};
 
 #if DEBUG_IDISPOSABLE
 				// After the scenario runs, validate all Responder-based instances
@@ -108,6 +111,8 @@ namespace UICatalog {
 #endif
 			}
 
+			Application.Shutdown ();
+
 #if DEBUG_IDISPOSABLE
 			// This proves that when the user exited the UI Catalog app
 			// it cleaned up properly.
@@ -230,13 +235,11 @@ namespace UICatalog {
 			_top.Add (_statusBar);
 			_top.Ready += () => {
 				if (_runningScenario != null) {
-					_top.SetFocus (_rightPane);
 					_runningScenario = null;
 				}
 			};
 
 			Application.Run (_top);
-			Application.Shutdown ();
 			return _runningScenario;
 		}
 

+ 15 - 11
UnitTests/ApplicationTests.cs

@@ -39,7 +39,7 @@ namespace Terminal.Gui {
 			Assert.Equal (80, Application.Driver.Cols);
 			Assert.Equal (25, Application.Driver.Rows);
 
-			Application.Shutdown (true);
+			Application.Shutdown ();
 			Assert.Null (Application.Current);
 			Assert.Null (Application.CurrentView);
 			Assert.Null (Application.Top);
@@ -73,7 +73,7 @@ namespace Terminal.Gui {
 
 		void Shutdown ()
 		{
-			Application.Shutdown (true);
+			Application.Shutdown ();
 		}
 
 		[Fact]
@@ -89,15 +89,19 @@ namespace Terminal.Gui {
 			var rs = Application.Begin (top);
 			Assert.NotNull (rs);
 			Assert.Equal (top, Application.Current);
-			Application.End (rs, true);
+			Application.End (rs);
 
 			Assert.Null (Application.Current);
 			Assert.Null (Application.CurrentView);
+			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);
-
-			Shutdown ();
 		}
 
 		[Fact]
@@ -115,9 +119,9 @@ namespace Terminal.Gui {
 				Application.RequestStop ();
 			};
 
-			Application.Run (top, true);
+			Application.Run (top);
 
-			Application.Shutdown (true);
+			Application.Shutdown ();
 			Assert.Null (Application.Current);
 			Assert.Null (Application.CurrentView);
 			Assert.Null (Application.Top);
@@ -140,9 +144,9 @@ namespace Terminal.Gui {
 				top.Running = false;
 			};
 
-			Application.Run (top, true);
+			Application.Run (top);
 
-			Application.Shutdown (true);
+			Application.Shutdown ();
 			Assert.Null (Application.Current);
 			Assert.Null (Application.CurrentView);
 			Assert.Null (Application.Top);
@@ -190,7 +194,7 @@ namespace Terminal.Gui {
 				keyUps++;
 			};
 
-			Application.Run (Application.Top, true);
+			Application.Run (Application.Top);
 
 			// Input string should match output
 			Assert.Equal (input, output);
@@ -201,7 +205,7 @@ namespace Terminal.Gui {
 			// # of key up events should match # of iterations
 			Assert.Equal (stackSize, iterations);
 
-			Application.Shutdown (true);
+			Application.Shutdown ();
 			Assert.Null (Application.Current);
 			Assert.Null (Application.CurrentView);
 			Assert.Null (Application.Top);

+ 3 - 0
UnitTests/ConsoleDriverTests.cs

@@ -11,6 +11,7 @@ namespace Terminal.Gui {
 		public void Init_Inits ()
 		{
 			var driver = new FakeDriver ();
+			Application.Init (driver, new NetMainLoop (() => FakeConsole.ReadKey (true)));
 			driver.Init (() => { });
 
 			Assert.Equal (80, Console.BufferWidth);
@@ -26,6 +27,7 @@ namespace Terminal.Gui {
 		public void End_Cleans_Up ()
 		{
 			var driver = new FakeDriver ();
+			Application.Init (driver, new NetMainLoop (() => FakeConsole.ReadKey (true)));
 			driver.Init (() => { });
 
 			FakeConsole.ForegroundColor = ConsoleColor.Red;
@@ -48,6 +50,7 @@ namespace Terminal.Gui {
 		public void SetColors_Changes_Colors ()
 		{
 			var driver = new FakeDriver ();
+			Application.Init (driver, new NetMainLoop (() => FakeConsole.ReadKey (true)));
 			driver.Init (() => { });
 			Assert.Equal (ConsoleColor.Gray, Console.ForegroundColor);
 			Assert.Equal (ConsoleColor.Black, Console.BackgroundColor);

+ 16 - 8
UnitTests/PosTests.cs

@@ -283,42 +283,50 @@ namespace Terminal.Gui {
 				return (win, button);
 			}
 
-			void cleanup ()
+			Application.RunState rs;
+
+			void cleanup (Application.RunState rs)
 			{
 				// Cleanup
-				Application.Shutdown ();
+				Application.End (rs);
 			}
 
 			// Test cases:
 			var app = setup ();
 			app.button.Y = Pos.Left (app.win);
+			rs = Application.Begin (Application.Top);
 			Application.Run ();
-			cleanup ();
+			cleanup (rs);
 
 			app = setup ();
 			app.button.Y = Pos.X (app.win);
+			rs = Application.Begin (Application.Top);
 			Application.Run ();
-			cleanup ();
+			cleanup (rs);
 
 			app = setup ();
 			app.button.Y = Pos.Top (app.win);
+			rs = Application.Begin (Application.Top);
 			Application.Run ();
-			cleanup ();
+			cleanup (rs);
 
 			app = setup ();
 			app.button.Y = Pos.Y (app.win);
+			rs = Application.Begin (Application.Top);
 			Application.Run ();
-			cleanup ();
+			cleanup (rs);
 
 			app = setup ();
 			app.button.Y = Pos.Bottom (app.win);
+			rs = Application.Begin (Application.Top);
 			Application.Run ();
-			cleanup ();
+			cleanup (rs);
 
 			app = setup ();
 			app.button.Y = Pos.Right (app.win);
+			rs = Application.Begin (Application.Top);
 			Application.Run ();
-			cleanup ();
+			cleanup (rs);
 
 		}
 

+ 4 - 2
UnitTests/ScenarioTests.cs

@@ -69,9 +69,10 @@ namespace Terminal.Gui {
 				var scenario = (Scenario)Activator.CreateInstance (scenarioClass);
 				scenario.Init (Application.Top, Colors.Base);
 				scenario.Setup ();
+				var rs = Application.Begin (Application.Top);
 				scenario.Run ();
 
-				Application.Shutdown ();
+				Application.End (rs);
 
 				Assert.Equal (0, abortCount);
 				// # of key up events should match # of iterations
@@ -124,9 +125,10 @@ namespace Terminal.Gui {
 			var scenario = (Scenario)Activator.CreateInstance (scenarioClass);
 			scenario.Init (Application.Top, Colors.Base);
 			scenario.Setup ();
+			var rs = Application.Begin (Application.Top);
 			scenario.Run ();
 
-			Application.Shutdown ();
+			Application.End (rs);
 
 			Assert.Equal (0, abortCount);
 			// # of key up events should match # of iterations

+ 4 - 4
UnitTests/ViewTests.cs

@@ -624,8 +624,8 @@ namespace Terminal.Gui {
 				t.Running = false;
 			};
 
-			Application.Run (t, true);
-			Application.Shutdown (true);
+			Application.Run (t);
+			Application.Shutdown ();
 
 			Assert.Equal (1, tc);
 			Assert.Equal (1, wc);
@@ -704,8 +704,8 @@ namespace Terminal.Gui {
 				t.Running = false;
 			};
 
-			Application.Run (t, true);
-			Application.Shutdown (true);
+			Application.Run (t);
+			Application.Shutdown ();
 
 			Assert.Equal (1, tc);
 			Assert.Equal (1, wc);