Browse Source

Merge pull request #553 from BDisp/driver-shutdown

Fixes #515, #518 and #536.
Charlie Kindel 5 years ago
parent
commit
353baf7dc2
3 changed files with 49 additions and 42 deletions
  1. 44 36
      Terminal.Gui/Core.cs
  2. 2 5
      UICatalog/Scenario.cs
  3. 3 1
      UICatalog/UICatalog.cs

+ 44 - 36
Terminal.Gui/Core.cs

@@ -2125,23 +2125,25 @@ namespace Terminal.Gui {
 		{
 			if (_initialized) return;
 
-			var p = Environment.OSVersion.Platform;
-			Mono.Terminal.IMainLoopDriver mainLoopDriver;
-
-			if (UseSystemConsole) {
-				mainLoopDriver = new Mono.Terminal.NetMainLoop ();
-				Driver = new NetDriver ();
-			} else if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
-				var windowsDriver = new WindowsDriver ();
-				mainLoopDriver = windowsDriver;
-				Driver = windowsDriver;
-			} else {
-				mainLoopDriver = new Mono.Terminal.UnixMainLoop ();
-				Driver = new CursesDriver ();
+			if (Driver == null) {
+				var p = Environment.OSVersion.Platform;
+				Mono.Terminal.IMainLoopDriver mainLoopDriver;
+
+				if (UseSystemConsole) {
+					mainLoopDriver = new Mono.Terminal.NetMainLoop ();
+					Driver = new NetDriver ();
+				} else if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
+					var windowsDriver = new WindowsDriver ();
+					mainLoopDriver = windowsDriver;
+					Driver = windowsDriver;
+				} else {
+					mainLoopDriver = new Mono.Terminal.UnixMainLoop ();
+					Driver = new CursesDriver ();
+				}
+				Driver.Init (TerminalResized);
+				MainLoop = new Mono.Terminal.MainLoop (mainLoopDriver);
+				SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext (MainLoop));
 			}
-			Driver.Init (TerminalResized);
-			MainLoop = new Mono.Terminal.MainLoop (mainLoopDriver);
-			SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext (MainLoop));
 			Top = topLevelFactory ();
 			Current = Top;
 			CurrentView = Top;
@@ -2152,6 +2154,8 @@ namespace Terminal.Gui {
 		/// Captures the execution state for the provided <see cref="Toplevel"/>  view.
 		/// </summary>
 		public class RunState : IDisposable {
+			internal bool closeDriver = true;
+
 			internal RunState (Toplevel view)
 			{
 				Toplevel = view;
@@ -2168,7 +2172,7 @@ namespace Terminal.Gui {
 			/// <see cref="Application.RunState"/> was occupying.</remarks>
 			public void Dispose ()
 			{
-				Dispose (true);
+				Dispose (closeDriver);
 				GC.SuppressFinalize (this);
 			}
 
@@ -2180,7 +2184,7 @@ namespace Terminal.Gui {
 			protected virtual void Dispose (bool disposing)
 			{
 				if (Toplevel != null) {
-					End (Toplevel);
+					End (Toplevel, disposing);
 					Toplevel = null;
 				}
 			}
@@ -2373,14 +2377,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)"/> method upon completion.</returns>
+		/// <returns>The runstate handle that needs to be passed to the <see cref="End(RunState, bool)"/> 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)"/> method upon termination which will
+		///  the <see cref="RunLoop"/> method, and then the <see cref="End(RunState, bool)"/> method upon termination which will
 		///   undo these changes.
 		/// </remarks>
 		public static RunState Begin (Toplevel toplevel)
@@ -2417,19 +2421,21 @@ 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>
-		public static void End (RunState runState)
+		/// <param name="closeDriver"><c>true</c>Closes the application.<c>false</c>Closes the toplevels only.</param>
+		public static void End (RunState runState, bool closeDriver = true)
 		{
 			if (runState == null)
 				throw new ArgumentNullException (nameof (runState));
 
+			runState.closeDriver = closeDriver;
 			runState.Dispose ();
-			runState = null;
 		}
 
 		/// <summary>
-		/// Shutdown an application initalized with <see cref="Init()"/>
+		/// Shutdown an application initialized with <see cref="Init()"/>
 		/// </summary>
-		public static void Shutdown ()
+		/// /// <param name="closeDriver"><c>true</c>Closes the application.<c>false</c>Closes toplevels only.</param>
+		public static void Shutdown (bool closeDriver = true)
 		{
 			// 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.
@@ -2443,10 +2449,12 @@ namespace Terminal.Gui {
 			CurrentView = null;
 			Top = null;
 
-			// 
-			MainLoop = null;
+			// Closes the application if it's true.
+			if (closeDriver) {
+				MainLoop = null;
+				Driver.End ();
+			}
 
-			Driver.End ();
 			_initialized = false;
 		}
 
@@ -2480,13 +2488,13 @@ namespace Terminal.Gui {
 			Driver.Refresh ();
 		}
 
-		internal static void End (View view)
+		internal static void End (View view, bool closeDriver = true)
 		{
 			if (toplevels.Peek () != view)
 				throw new ArgumentException ("The view that you end with must be balanced");
 			toplevels.Pop ();
 			if (toplevels.Count == 0)
-				Shutdown ();
+				Shutdown (closeDriver);
 			else {
 				Current = toplevels.Peek ();
 				Refresh ();
@@ -2545,7 +2553,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Runs the application by calling <see cref="Run(Toplevel)"/> with the value of <see cref="Top"/>
+		/// Runs the application by calling <see cref="Run(Toplevel, bool)"/> with the value of <see cref="Top"/>
 		/// </summary>
 		public static void Run ()
 		{
@@ -2553,7 +2561,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Runs the application by calling <see cref="Run(Toplevel)"/> with a new instance of the specified <see cref="Toplevel"/>-derived class
+		/// Runs the application by calling <see cref="Run(Toplevel, bool)"/> with a new instance of the specified <see cref="Toplevel"/>-derived class
 		/// </summary>
 		public static void Run<T> () where T : Toplevel, new()
 		{
@@ -2571,11 +2579,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)"/> stop execution, call <see cref="Application.RequestStop"/>.
+		///     To make a <see cref="Run(Toplevel, bool)"/> stop execution, call <see cref="Application.RequestStop"/>.
 		///   </para>
 		///   <para>
-		///     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)"/>.
+		///     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)"/>.
 		///   </para>
 		///   <para>
 		///     Alternatively, to have a program control the main loop and 
@@ -2585,11 +2593,11 @@ namespace Terminal.Gui {
 		///     then return control immediately.
 		///   </para>
 		/// </remarks>
-		public static void Run (Toplevel view)
+		public static void Run (Toplevel view, bool closeDriver = true)
 		{
 			var runToken = Begin (view);
 			RunLoop (runToken);
-			End (runToken);
+			End (runToken, closeDriver);
 		}
 
 		/// <summary>

+ 2 - 5
UICatalog/Scenario.cs

@@ -195,11 +195,8 @@ namespace UICatalog {
 		/// </remarks>
 		public virtual void Run ()
 		{
-			Application.Run (Top);
-
-			// Every call to Application.Init must be bound by a call to Shutdown
-			// or Init doesn't do anything
-			Application.Shutdown ();
+			// This method already performs a later automatic shutdown.
+			Application.Run (Top, false);
 		}
 
 		/// <summary>

+ 3 - 1
UICatalog/UICatalog.cs

@@ -79,6 +79,8 @@ namespace UICatalog {
 				scenario.Run ();
 				scenario = GetScenarioToRun ();
 			}
+			// Now closes the driver too.
+			Application.Shutdown ();
 		}
 
 		/// <summary>
@@ -197,7 +199,7 @@ namespace UICatalog {
 			};
 #endif
 			
-			Application.Run (_top);
+			Application.Run (_top, false);
 			return _runningScenario;
 		}