Browse Source

Fixes #1020. NetDriver does not deal well with unicode characters yet.

BDisp 4 years ago
parent
commit
415cd11e20

+ 3 - 1
Example/demo.cs

@@ -562,9 +562,11 @@ static class Demo {
 		if (Debugger.IsAttached)
 			CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US");
 
-		//Application.UseSystemConsole = true;
+		Application.UseSystemConsole = true;
 
 		Application.Init();
+		Application.HeightSize = HeightSize.BufferHeight;
+		//ConsoleDriver.Diagnostics = ConsoleDriver.DiagnosticFlags.FramePadding | ConsoleDriver.DiagnosticFlags.FrameRuler;
 
 		var top = Application.Top;
 

+ 2 - 0
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -20,6 +20,7 @@ namespace Terminal.Gui {
 		public override int Cols => Curses.Cols;
 		public override int Rows => Curses.Lines;
 		public override int Top => 0;
+		public override HeightSize HeightSize { get; set; }
 
 		// Current row, and current col, tracked by Move/AddRune only
 		int ccol, crow;
@@ -70,6 +71,7 @@ namespace Terminal.Gui {
 		public override void Refresh () {
 			Curses.refresh ();
 			if (Curses.CheckWinChange ()) {
+				Clip = new Rect (0, 0, Cols, Rows);
 				TerminalResized?.Invoke ();
 			}
 		}

+ 1 - 0
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs

@@ -20,6 +20,7 @@ namespace Terminal.Gui {
 		public override int Cols => cols;
 		public override int Rows => rows;
 		public override int Top => 0;
+		public override HeightSize HeightSize { get; set; }
 
 		// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
 		int [,,] contents;

+ 129 - 66
Terminal.Gui/ConsoleDrivers/NetDriver.cs

@@ -18,17 +18,12 @@ namespace Terminal.Gui {
 		public override int Cols => cols;
 		public override int Rows => rows;
 		public override int Top => top;
+		public override HeightSize HeightSize { get; set; }
 
 		// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
 		int [,,] contents;
 		bool [] dirtyLine;
 
-		public NetDriver ()
-		{
-			ResizeScreen ();
-			UpdateOffscreen ();
-		}
-
 		void UpdateOffscreen ()
 		{
 			int cols = Cols;
@@ -48,53 +43,39 @@ namespace Terminal.Gui {
 
 		static bool sync = false;
 
-		bool needMove;
 		// Current row, and current col, tracked by Move/AddCh only
 		int ccol, crow;
 		public override void Move (int col, int row)
 		{
 			ccol = col;
 			crow = row;
-
-			if (Clip.Contains (col, row)) {
-				if (cols == Console.WindowWidth && rows == Console.WindowHeight) {
-					Console.SetCursorPosition (col, row);
-					needMove = false;
-				}
-			} else {
-				if (cols == Console.WindowWidth && rows == Console.WindowHeight) {
-					if (Console.WindowHeight > 0) {
-						Console.SetCursorPosition (Clip.X, Clip.Y);
-					}
-					needMove = true;
-				}
-			}
 		}
 
 		public override void AddRune (Rune rune)
 		{
 			rune = MakePrintable (rune);
 			if (Clip.Contains (ccol, crow)) {
-				if (needMove) {
-					if (cols == Console.WindowWidth && rows == Console.WindowHeight) {
-						Console.SetCursorPosition (ccol, crow);
-					}
-					needMove = false;
-				}
 				contents [crow, ccol, 0] = (int)(uint)rune;
 				contents [crow, ccol, 1] = currentAttribute;
 				contents [crow, ccol, 2] = 1;
 				dirtyLine [crow] = true;
-			} else
-				needMove = true;
+			}
 			ccol++;
+			var runeWidth = Rune.ColumnWidth (rune);
+			if (runeWidth > 1) {
+				for (int i = 1; i < runeWidth; i++) {
+					contents [crow, ccol, 2] = 0;
+					ccol++;
+				}
+			}
 			//if (ccol == Cols) {
 			//	ccol = 0;
 			//	if (crow + 1 < Rows)
 			//		crow++;
 			//}
-			if (sync)
+			if (sync) {
 				UpdateScreen ();
+			}
 		}
 
 		public override void AddStr (ustring str)
@@ -122,10 +103,16 @@ namespace Terminal.Gui {
 			return new Attribute () { value = ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff) };
 		}
 
+		bool isWinPlatform;
+
 		public override void Init (Action terminalResized)
 		{
 			TerminalResized = terminalResized;
 			Console.TreatControlCAsInput = true;
+			var p = Environment.OSVersion.Platform;
+			if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
+				isWinPlatform = true;
+			}
 
 			Colors.TopLevel = new ColorScheme ();
 			Colors.Base = new ColorScheme ();
@@ -164,14 +151,64 @@ namespace Terminal.Gui {
 			Colors.Error.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Red);
 			Colors.Error.HotFocus = Colors.Error.HotNormal;
 			Clear ();
+			ResizeScreen ();
+			UpdateOffscreen ();
 		}
 
 		void ResizeScreen ()
 		{
-			cols = Console.WindowWidth;
-			rows = Console.WindowHeight;
+			const int Min_WindowWidth = 14;
+
+			switch (HeightSize) {
+			case HeightSize.WindowHeight:
+				if (Console.WindowHeight > 0) {
+					// Can raise an exception while is still resizing.
+					try {
+						// Not supported on Unix.
+						if (isWinPlatform) {
+							Console.CursorTop = 0;
+							Console.CursorLeft = 0;
+							Console.WindowTop = 0;
+							Console.WindowLeft = 0;
+							Console.SetBufferSize (Math.Max (Min_WindowWidth, Console.WindowWidth),
+								Console.WindowHeight);
+						} else {
+							//Console.Out.Write ($"\x1b[8;{Console.WindowHeight};{Console.WindowWidth}t");
+							//Console.Out.Flush ();
+							Console.Out.Write ($"\x1b[0;0" +
+								$";{Console.WindowHeight};" +
+								$"{Math.Max (Min_WindowWidth, Console.WindowWidth)}w");
+						}
+					} catch (System.IO.IOException) {
+						return;
+					} catch (ArgumentOutOfRangeException) {
+						return;
+					}
+				}
+				cols = Console.WindowWidth;
+				rows = Console.WindowHeight;
+				top = 0;
+				break;
+			case HeightSize.BufferHeight:
+				if (isWinPlatform && Console.WindowHeight > 0) {
+					if (isWinPlatform) {
+						// Can raise an exception while is still resizing.
+						try {
+							Console.WindowTop = Math.Max (Math.Min (top, Console.BufferHeight - Console.WindowHeight), 0);
+						} catch (Exception) {
+							return;
+						}
+					}
+				} else {
+					Console.Out.Write ($"\x1b[{top};{Console.WindowLeft}" +
+						$";{Console.BufferHeight}" +
+						$";{Math.Max (Min_WindowWidth, Console.BufferWidth)}w");
+				}
+				cols = Console.BufferWidth;
+				rows = Console.BufferHeight;
+				break;
+			}
 			Clip = new Rect (0, 0, Cols, Rows);
-			top = Console.WindowTop;
 		}
 
 		public override Attribute MakeAttribute (Color fore, Color back)
@@ -196,29 +233,40 @@ namespace Terminal.Gui {
 
 		public override void UpdateScreen ()
 		{
-			if (Rows == 0) {
+			if (winChanging || Console.WindowHeight == 0
+				|| (HeightSize == HeightSize.WindowHeight && Rows != Console.WindowHeight)
+				|| (HeightSize == HeightSize.BufferHeight && Rows != Console.BufferHeight)) {
 				return;
 			}
 
-			int rows = Rows;
+			int top = Top;
+			int rows = Math.Min (Console.WindowHeight + top, Rows);
 			int cols = Cols;
 
 			for (int row = top; row < rows; row++) {
-				if (!dirtyLine [row])
+				if (!dirtyLine [row]) {
 					continue;
+				}
 				dirtyLine [row] = false;
+				int [,,] damage = new int [0, 0, 0];
 				for (int col = 0; col < cols; col++) {
-					if (contents [row, col, 2] != 1)
+					if (contents [row, col, 2] != 1) {
 						continue;
+					}
 
 					if (Console.WindowHeight > 0) {
-						Console.SetCursorPosition (col, row);
+						// Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth.
+						try {
+							Console.SetCursorPosition (col, row);
+						} catch (Exception) {
+							return;
+						}
 					}
 					for (; col < cols && contents [row, col, 2] == 1; col++) {
 						var color = contents [row, col, 1];
-						if (color != redrawColor)
+						if (color != redrawColor) {
 							SetColor (color);
-
+						}
 						Console.Write ((char)contents [row, col, 0]);
 						contents [row, col, 2] = 0;
 					}
@@ -230,12 +278,6 @@ namespace Terminal.Gui {
 
 		public override void Refresh ()
 		{
-			if (Console.WindowWidth != Cols || Console.WindowHeight != Rows || Console.WindowTop != Top) {
-				ResizeScreen ();
-				UpdateOffscreen ();
-				TerminalResized.Invoke ();
-			}
-
 			UpdateScreen ();
 		}
 
@@ -243,10 +285,12 @@ namespace Terminal.Gui {
 		{
 			// Prevents the exception of size changing during resizing.
 			try {
-				if (ccol > 0 && ccol < Console.WindowWidth && crow > 0 && crow < Console.WindowHeight) {
+				if (ccol >= 0 && ccol <= cols && crow >= 0 && crow <= rows) {
 					Console.SetCursorPosition (ccol, crow);
 				}
-			} catch (ArgumentOutOfRangeException) { }
+			} catch (System.IO.IOException) {
+			} catch (ArgumentOutOfRangeException) {
+			}
 		}
 
 		public override void StartReportingMouseMoves ()
@@ -377,10 +421,11 @@ namespace Terminal.Gui {
 				keyModifiers.Alt = true;
 		}
 
+		bool winChanging;
 		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
 		{
 			// Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called
-			(mainLoop.Driver as NetMainLoop).KeyPressed = delegate (ConsoleKeyInfo consoleKey) {
+			(mainLoop.Driver as NetMainLoop).KeyPressed = (consoleKey) => {
 				var map = MapKey (consoleKey);
 				if (map == (Key)0xffffffff) {
 					return;
@@ -389,16 +434,23 @@ namespace Terminal.Gui {
 				keyUpHandler (new KeyEvent (map, keyModifiers));
 				keyModifiers = null;
 			};
+
+			(mainLoop.Driver as NetMainLoop).WinChanged = (e) => {
+				winChanging = true;
+				top = e;
+				ResizeScreen ();
+				UpdateOffscreen ();
+				winChanging = false;
+				TerminalResized.Invoke ();
+			};
 		}
 
 		public override void SetColors (ConsoleColor foreground, ConsoleColor background)
 		{
-			throw new NotImplementedException ();
 		}
 
 		public override void SetColors (short foregroundColorId, short backgroundColorId)
 		{
-			throw new NotImplementedException ();
 		}
 
 		public override void CookMouse ()
@@ -424,14 +476,15 @@ namespace Terminal.Gui {
 	/// <remarks>
 	/// This implementation is used for NetDriver.
 	/// </remarks>
-	public class NetMainLoop : IMainLoopDriver {
+	internal class NetMainLoop : IMainLoopDriver {
 		ManualResetEventSlim keyReady = new ManualResetEventSlim (false);
 		ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
 		ManualResetEventSlim winChange = new ManualResetEventSlim (false);
-		ConsoleKeyInfo? keyResult = null;
+		Queue<ConsoleKeyInfo?> keyResult = new Queue<ConsoleKeyInfo?> ();
 		MainLoop mainLoop;
 		ConsoleDriver consoleDriver;
 		bool winChanged;
+		int newTop;
 		CancellationTokenSource tokenSource = new CancellationTokenSource ();
 
 		/// <summary>
@@ -439,6 +492,8 @@ namespace Terminal.Gui {
 		/// </summary>
 		public Action<ConsoleKeyInfo> KeyPressed;
 
+		public Action<int> WinChanged;
+
 		/// <summary>
 		/// Initializes the class with the console driver.
 		/// </summary>
@@ -449,7 +504,7 @@ namespace Terminal.Gui {
 		public NetMainLoop (ConsoleDriver consoleDriver = null)
 		{
 			if (consoleDriver == null) {
-				throw new ArgumentNullException ("console driver instance must be provided.");
+				throw new ArgumentNullException ("Console driver instance must be provided.");
 			}
 			this.consoleDriver = consoleDriver;
 		}
@@ -459,7 +514,9 @@ namespace Terminal.Gui {
 			while (true) {
 				waitForProbe.Wait ();
 				waitForProbe.Reset ();
-				keyResult = Console.ReadKey (true);
+				if (keyResult.Count == 0) {
+					keyResult.Enqueue (Console.ReadKey (true));
+				}
 				keyReady.Set ();
 			}
 		}
@@ -478,9 +535,19 @@ namespace Terminal.Gui {
 		void WaitWinChange ()
 		{
 			while (true) {
-				if (Console.WindowWidth != consoleDriver.Cols || Console.WindowHeight != consoleDriver.Rows
-					|| Console.WindowTop != consoleDriver.Top) { // Top only working on Windows.
-					return;
+				switch (consoleDriver.HeightSize) {
+				case HeightSize.WindowHeight:
+					if (Console.WindowWidth != consoleDriver.Cols || Console.WindowHeight != consoleDriver.Rows) {
+						return;
+					}
+					break;
+				case HeightSize.BufferHeight:
+					if (Console.BufferWidth != consoleDriver.Cols || Console.BufferHeight != consoleDriver.Rows
+						|| Console.WindowTop != consoleDriver.Top) {
+						newTop = Console.WindowTop;
+						return;
+					}
+					break;
 				}
 			}
 		}
@@ -499,8 +566,6 @@ namespace Terminal.Gui {
 
 		bool IMainLoopDriver.EventsPending (bool wait)
 		{
-			long now = DateTime.UtcNow.Ticks;
-
 			waitForProbe.Set ();
 			winChange.Set ();
 
@@ -519,7 +584,7 @@ namespace Terminal.Gui {
 			}
 
 			if (!tokenSource.IsCancellationRequested) {
-				return keyResult.HasValue || CheckTimers (wait, out _) || winChanged;
+				return keyResult.Count > 0 || CheckTimers (wait, out _) || winChanged;
 			}
 
 			tokenSource.Dispose ();
@@ -552,14 +617,12 @@ namespace Terminal.Gui {
 
 		void IMainLoopDriver.MainIteration ()
 		{
-			if (keyResult.HasValue) {
-				var kr = keyResult;
-				keyResult = null;
-				KeyPressed?.Invoke (kr.Value);
+			if (keyResult.Count > 0) {
+				KeyPressed?.Invoke (keyResult.Dequeue ().Value);
 			}
 			if (winChanged) {
 				winChanged = false;
-				consoleDriver.Refresh ();
+				WinChanged.Invoke (newTop);
 			}
 		}
 	}

+ 198 - 133
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -207,7 +207,7 @@ namespace Terminal.Gui {
 			public override string ToString () => $"({X},{Y})";
 		};
 
-		internal struct WindowBufferSizeRecord {
+		public struct WindowBufferSizeRecord {
 			public Coordinate size;
 
 			public WindowBufferSizeRecord (short x, short y)
@@ -355,6 +355,20 @@ namespace Terminal.Gui {
 			}
 		}
 
+		[StructLayout (LayoutKind.Sequential)]
+		public struct ConsoleKeyInfoEx {
+			public ConsoleKeyInfo consoleKeyInfo;
+			public bool CapsLock;
+			public bool NumLock;
+
+			public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock)
+			{
+				this.consoleKeyInfo = consoleKeyInfo;
+				CapsLock = capslock;
+				NumLock = numlock;
+			}
+		}
+
 		[DllImport ("kernel32.dll", SetLastError = true)]
 		static extern IntPtr GetStdHandle (int nStdHandle);
 
@@ -431,14 +445,15 @@ namespace Terminal.Gui {
 
 				return numberEventsRead == 0
 					? null
-					: new [] {Marshal.PtrToStructure<InputRecord> (pRecord)};
+					: new [] { Marshal.PtrToStructure<InputRecord> (pRecord) };
 			} catch (Exception) {
 				return null;
 			} finally {
 				Marshal.FreeHGlobal (pRecord);
 			}
 		}
-#if false	// Not needed on the constructor. Perhaps could be used on resizing. To study.
+
+#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 ();
 
@@ -498,19 +513,17 @@ namespace Terminal.Gui {
 #endif
 	}
 
-	internal class WindowsDriver : ConsoleDriver, IMainLoopDriver {
+	internal class WindowsDriver : ConsoleDriver {
 		static bool sync = false;
-		ManualResetEventSlim eventReady = new ManualResetEventSlim (false);
-		ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
-		MainLoop mainLoop;
 		WindowsConsole.CharInfo [] OutputBuffer;
-		int cols, rows;
+		int cols, rows, top;
 		WindowsConsole winConsole;
 		WindowsConsole.SmallRect damageRegion;
 
 		public override int Cols => cols;
 		public override int Rows => rows;
-		public override int Top => 0;
+		public override int Top => top;
+		public override HeightSize HeightSize { get; set; }
 
 		public WindowsDriver ()
 		{
@@ -527,8 +540,11 @@ namespace Terminal.Gui {
 
 			ResizeScreen ();
 			UpdateOffScreen ();
+		}
 
-			Task.Run ((Action)WindowsInputHandler);
+		public WindowsConsole WinConsole {
+			get => winConsole;
+			private set => winConsole = value;
 		}
 
 		private void SetupColorsAndBorders ()
@@ -566,98 +582,6 @@ namespace Terminal.Gui {
 			Colors.Error.HotFocus = MakeColor (ConsoleColor.Black, ConsoleColor.DarkRed);
 		}
 
-		[StructLayout (LayoutKind.Sequential)]
-		public struct ConsoleKeyInfoEx {
-			public ConsoleKeyInfo consoleKeyInfo;
-			public bool CapsLock;
-			public bool NumLock;
-
-			public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock)
-			{
-				this.consoleKeyInfo = consoleKeyInfo;
-				CapsLock = capslock;
-				NumLock = numlock;
-			}
-		}
-
-		// The records that we keep fetching
-		WindowsConsole.InputRecord [] result, records = new WindowsConsole.InputRecord [1];
-
-		void WindowsInputHandler ()
-		{
-			while (true) {
-				waitForProbe.Wait ();
-				waitForProbe.Reset ();
-
-				result = winConsole.ReadConsoleInput ();
-
-				eventReady.Set ();
-			}
-		}
-
-		void IMainLoopDriver.Setup (MainLoop mainLoop)
-		{
-			this.mainLoop = mainLoop;
-		}
-
-		void IMainLoopDriver.Wakeup ()
-		{
-			//tokenSource.Cancel ();
-			eventReady.Reset ();
-			eventReady.Set ();
-		}
-
-		bool IMainLoopDriver.EventsPending (bool wait)
-		{
-			if (CheckTimers (wait, out var waitTimeout)) {
-				return true;
-			}
-
-			result = null;
-			waitForProbe.Set ();
-
-			try {
-				if (!tokenSource.IsCancellationRequested) {
-					eventReady.Wait (waitTimeout, tokenSource.Token);
-				}
-			} catch (OperationCanceledException) {
-				return true;
-			} finally {
-				eventReady.Reset ();
-			}
-
-			if (!tokenSource.IsCancellationRequested) {
-				return result != null || CheckTimers (wait, out waitTimeout);
-			}
-
-			tokenSource.Dispose ();
-			tokenSource = new CancellationTokenSource ();
-			return true;
-		}
-
-		bool CheckTimers (bool wait, out int waitTimeout)
-		{
-			long now = DateTime.UtcNow.Ticks;
-
-			if (mainLoop.timeouts.Count > 0) {
-				waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond);
-				if (waitTimeout < 0)
-					return true;
-			} else {
-				waitTimeout = -1;
-			}
-
-			if (!wait)
-				waitTimeout = 0;
-
-			int ic;
-			lock (mainLoop.idleHandlers) {
-				ic = mainLoop.idleHandlers.Count;
-			}
-
-			return ic > 0;
-		}
-
 		Action<KeyEvent> keyHandler;
 		Action<KeyEvent> keyDownHandler;
 		Action<KeyEvent> keyUpHandler;
@@ -669,14 +593,12 @@ namespace Terminal.Gui {
 			this.keyDownHandler = keyDownHandler;
 			this.keyUpHandler = keyUpHandler;
 			this.mouseHandler = mouseHandler;
+
+			(mainLoop.Driver as WindowsMainLoop).ProcessInput = (e) => ProcessInput (e);
 		}
 
-		void IMainLoopDriver.MainIteration ()
+		void ProcessInput (WindowsConsole.InputRecord inputEvent)
 		{
-			if (result == null)
-				return;
-
-			var inputEvent = result [0];
 			switch (inputEvent.EventType) {
 			case WindowsConsole.EventType.Key:
 				var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent));
@@ -775,11 +697,7 @@ namespace Terminal.Gui {
 
 			case WindowsConsole.EventType.Focus:
 				break;
-
-			default:
-				break;
 			}
-			result = null;
 		}
 
 		WindowsConsole.ButtonState? LastMouseButtonPressed = null;
@@ -871,23 +789,23 @@ namespace Terminal.Gui {
 					Y = mouseEvent.MousePosition.Y
 				};
 				//if (p == point) {
-					switch (LastMouseButtonPressed) {
-					case WindowsConsole.ButtonState.Button1Pressed:
-						mouseFlag = MouseFlags.Button1Clicked;
-						break;
+				switch (LastMouseButtonPressed) {
+				case WindowsConsole.ButtonState.Button1Pressed:
+					mouseFlag = MouseFlags.Button1Clicked;
+					break;
 
-					case WindowsConsole.ButtonState.Button2Pressed:
-						mouseFlag = MouseFlags.Button2Clicked;
-						break;
+				case WindowsConsole.ButtonState.Button2Pressed:
+					mouseFlag = MouseFlags.Button2Clicked;
+					break;
 
-					case WindowsConsole.ButtonState.RightmostButtonPressed:
-						mouseFlag = MouseFlags.Button3Clicked;
-						break;
-					}
-					point = new Point () {
-						X = mouseEvent.MousePosition.X,
-						Y = mouseEvent.MousePosition.Y
-					};
+				case WindowsConsole.ButtonState.RightmostButtonPressed:
+					mouseFlag = MouseFlags.Button3Clicked;
+					break;
+				}
+				point = new Point () {
+					X = mouseEvent.MousePosition.X,
+					Y = mouseEvent.MousePosition.Y
+				};
 				//} else {
 				//	mouseFlag = 0;
 				//}
@@ -1009,7 +927,7 @@ namespace Terminal.Gui {
 
 		KeyModifiers keyModifiers;
 
-		public ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEventRecord keyEvent)
+		public WindowsConsole.ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEventRecord keyEvent)
 		{
 			var state = keyEvent.dwControlKeyState;
 
@@ -1036,10 +954,10 @@ namespace Terminal.Gui {
 				keyModifiers.Scrolllock = scrolllock;
 
 			var ConsoleKeyInfo = new ConsoleKeyInfo (keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control);
-			return new ConsoleKeyInfoEx (ConsoleKeyInfo, capslock, numlock);
+			return new WindowsConsole.ConsoleKeyInfoEx (ConsoleKeyInfo, capslock, numlock);
 		}
 
-		public Key MapKey (ConsoleKeyInfoEx keyInfoEx)
+		public Key MapKey (WindowsConsole.ConsoleKeyInfoEx keyInfoEx)
 		{
 			var keyInfo = keyInfoEx.consoleKeyInfo;
 			switch (keyInfo.Key) {
@@ -1245,7 +1163,6 @@ namespace Terminal.Gui {
 		}
 
 		int currentAttribute;
-		CancellationTokenSource tokenSource = new CancellationTokenSource ();
 
 		public override void SetAttribute (Attribute c)
 		{
@@ -1325,7 +1242,7 @@ namespace Terminal.Gui {
 			winConsole.Cleanup ();
 		}
 
-#region Unused
+		#region Unused
 		public override void SetColors (ConsoleColor foreground, ConsoleColor background)
 		{
 		}
@@ -1353,8 +1270,156 @@ namespace Terminal.Gui {
 		public override void CookMouse ()
 		{
 		}
-#endregion
-
+		#endregion
 	}
 
+	/// <summary>
+	/// Mainloop intended to be used with the <see cref="WindowsDriver"/>, and can
+	/// only be used on Windows.
+	/// </summary>
+	/// <remarks>
+	/// This implementation is used for WindowsDriver.
+	/// </remarks>
+	internal class WindowsMainLoop : IMainLoopDriver {
+		ManualResetEventSlim eventReady = new ManualResetEventSlim (false);
+		ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
+		ManualResetEventSlim winChange = new ManualResetEventSlim (false);
+		MainLoop mainLoop;
+		ConsoleDriver consoleDriver;
+		WindowsConsole winConsole;
+		bool winChanged;
+		CancellationTokenSource tokenSource = new CancellationTokenSource ();
+
+		// The records that we keep fetching
+		WindowsConsole.InputRecord [] result = new WindowsConsole.InputRecord [1];
+
+		/// <summary>
+		/// Invoked when a Key is pressed or released.
+		/// </summary>
+		public Action<WindowsConsole.InputRecord> ProcessInput;
+
+		public WindowsMainLoop (ConsoleDriver consoleDriver = null)
+		{
+			this.consoleDriver = consoleDriver;
+			winConsole = ((WindowsDriver)consoleDriver).WinConsole;
+		}
+
+		void IMainLoopDriver.Setup (MainLoop mainLoop)
+		{
+			this.mainLoop = mainLoop;
+			Task.Run ((Action)WindowsInputHandler);
+			Task.Run (CheckWinChange);
+		}
+
+		void WindowsInputHandler ()
+		{
+			while (true) {
+				waitForProbe.Wait ();
+				waitForProbe.Reset ();
+
+				result = winConsole.ReadConsoleInput ();
+
+				eventReady.Set ();
+			}
+		}
+
+		void CheckWinChange ()
+		{
+			while (true) {
+				winChange.Wait ();
+				winChange.Reset ();
+				WaitWinChange ();
+				winChanged = true;
+				eventReady.Set ();
+			}
+		}
+
+		void WaitWinChange ()
+		{
+			while (true) {
+				switch (consoleDriver.HeightSize) {
+				case HeightSize.WindowHeight:
+					if (Console.WindowWidth != consoleDriver.Cols || Console.WindowHeight != consoleDriver.Rows
+						|| Console.WindowTop != consoleDriver.Top) {    // Top only working on Windows.
+						return;
+					}
+					break;
+				case HeightSize.BufferHeight:
+					if (Console.BufferWidth != consoleDriver.Cols || Console.BufferHeight != consoleDriver.Rows) {
+						return;
+					}
+					break;
+				}
+			}
+		}
+
+		void IMainLoopDriver.Wakeup ()
+		{
+			//tokenSource.Cancel ();
+			eventReady.Reset ();
+			eventReady.Set ();
+		}
+
+		bool IMainLoopDriver.EventsPending (bool wait)
+		{
+			if (CheckTimers (wait, out var waitTimeout)) {
+				return true;
+			}
+
+			//result = null;
+			waitForProbe.Set ();
+			winChange.Set ();
+
+			try {
+				if (!tokenSource.IsCancellationRequested) {
+					eventReady.Wait (waitTimeout, tokenSource.Token);
+				}
+			} catch (OperationCanceledException) {
+				return true;
+			} finally {
+				eventReady.Reset ();
+			}
+
+			if (!tokenSource.IsCancellationRequested) {
+				return result != null || CheckTimers (wait, out waitTimeout);
+			}
+
+			tokenSource.Dispose ();
+			tokenSource = new CancellationTokenSource ();
+			return true;
+		}
+
+		bool CheckTimers (bool wait, out int waitTimeout)
+		{
+			long now = DateTime.UtcNow.Ticks;
+
+			if (mainLoop.timeouts.Count > 0) {
+				waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond);
+				if (waitTimeout < 0)
+					return true;
+			} else {
+				waitTimeout = -1;
+			}
+
+			if (!wait)
+				waitTimeout = 0;
+
+			int ic;
+			lock (mainLoop.idleHandlers) {
+				ic = mainLoop.idleHandlers.Count;
+			}
+
+			return ic > 0;
+		}
+
+		void IMainLoopDriver.MainIteration ()
+		{
+			if (result == null)
+				return;
+
+			var inputEvent = result [0];
+			result = null;
+			ProcessInput.Invoke (inputEvent);
+		}
+	}
 }

+ 26 - 6
Terminal.Gui/Core/Application.cs

@@ -59,7 +59,7 @@ namespace Terminal.Gui {
 		/// The current <see cref="ConsoleDriver"/> in use.
 		/// </summary>
 		public static ConsoleDriver Driver;
-
+		
 		/// <summary>
 		/// The <see cref="Toplevel"/> object used for the application on startup (<seealso cref="Application.Top"/>)
 		/// </summary>
@@ -73,11 +73,32 @@ namespace Terminal.Gui {
 		public static Toplevel Current { get; private set; }
 
 		/// <summary>
-		/// TThe current <see cref="View"/> object being redrawn.
+		/// The current <see cref="View"/> object being redrawn.
 		/// </summary>
 		/// /// <value>The current.</value>
 		public static View CurrentView { get; set; }
 
+		/// <summary>
+		/// The current <see cref="Terminal.Gui.HeightSize"/> used in the terminal.
+		/// </summary>
+		public static HeightSize HeightSize {
+			get {
+				if (Driver == null) {
+					throw new ArgumentNullException ("The driver must be initialized first.");
+				}
+				return Driver.HeightSize;
+			}
+			set {
+				if (Driver == null) {
+					throw new ArgumentNullException ("The driver must be initialized first.");
+				}
+				if (Driver.HeightSize != value) {
+					Driver.HeightSize = value;
+					Driver.Refresh ();
+				}
+			}
+		}
+
 		/// <summary>
 		/// The <see cref="MainLoop"/>  driver for the application
 		/// </summary>
@@ -167,7 +188,7 @@ namespace Terminal.Gui {
 		static void Init (Func<Toplevel> topLevelFactory, ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null)
 		{
 			if (_initialized && driver == null) return;
-
+			
 			// Used only for start debugging on Unix.
 //#if DEBUG
 //			while (!System.Diagnostics.Debugger.IsAttached) {
@@ -193,9 +214,8 @@ namespace Terminal.Gui {
 					Driver = new NetDriver ();
 					mainLoopDriver = new NetMainLoop (Driver);
 				} else if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
-					var windowsDriver = new WindowsDriver ();
-					mainLoopDriver = windowsDriver;
-					Driver = windowsDriver;
+					Driver = new WindowsDriver ();
+					mainLoopDriver = new WindowsMainLoop (Driver);
 				} else {
 					mainLoopDriver = new UnixMainLoop ();
 					Driver = new CursesDriver ();

+ 19 - 0
Terminal.Gui/Core/ConsoleDriver.cs

@@ -458,6 +458,20 @@ namespace Terminal.Gui {
 		public static Dictionary<string, ColorScheme> ColorSchemes { get; }
 	}
 
+	/// <summary>
+	/// The visible height should be used on the window.
+	/// </summary>
+	public enum HeightSize {
+		/// <summary>
+		/// Only window height will be visible not allowing scroll.
+		/// </summary>
+		WindowHeight,
+		/// <summary>
+		/// All buffer height will be visible allowing scroll.
+		/// </summary>
+		BufferHeight
+	}
+
 	///// <summary>
 	///// Special characters that can be drawn with 
 	///// </summary>
@@ -546,6 +560,11 @@ namespace Terminal.Gui {
 		/// </summary>
 		public abstract int Top { get; }
 
+		/// <summary>
+		/// The current <see cref="HeightSize"/> used in the terminal.
+		/// </summary>
+		public abstract HeightSize HeightSize { get; set; }
+
 		/// <summary>
 		/// Initializes the driver
 		/// </summary>

+ 26 - 14
UICatalog/Scenarios/CharacterMap.cs

@@ -1,4 +1,7 @@
-using NStack;
+#define DRAW_CONTENT
+//#define BASE_DRAW_CONTENT
+
+using NStack;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
@@ -58,15 +61,19 @@ namespace UICatalog {
 				CreateRadio("End", CharMap.MaxCodePointVal - 16, CharMap.MaxCodePointVal),
 			};
 
-			var jumpList = new RadioGroup (radioItems.Select (t => t.radioLabel).ToArray ());
-			jumpList.X = Pos.X (label);
-			jumpList.Y = Pos.Bottom (label);
-			jumpList.Width = Dim.Fill ();
+			var jumpList = new RadioGroup (radioItems.Select (t => t.radioLabel).ToArray ()) {
+				X = Pos.X (label),
+				Y = Pos.Bottom (label),
+				Width = Dim.Fill (),
+				SelectedItem = 8
+			};
 			jumpList.SelectedItemChanged += (args) => {
-				_charMap.Start = radioItems[args.SelectedItem].start;
+				_charMap.Start = radioItems [args.SelectedItem].start;
 			};
 
 			Win.Add (jumpList);
+
+			jumpList.Refresh ();
 		}
 
 		public override void Run ()
@@ -91,11 +98,14 @@ namespace UICatalog {
 		}
 		int _start = 0x2500;
 
+		public const int H_SPACE = 2;
+		public const int V_SPACE = 2;
+
 		public static int MaxCodePointVal => 0xE0FFF;
 
 		// Row Header + space + (space + char + space)
 		public static int RowHeaderWidth => $"U+{MaxCodePointVal:x5}".Length;
-		public static int RowWidth => RowHeaderWidth + (" c".Length * 16);
+		public static int RowWidth => RowHeaderWidth + (H_SPACE * 16);
 
 		public CharMap ()
 		{
@@ -109,10 +119,12 @@ namespace UICatalog {
 					ShowHorizontalScrollIndicator = false;
 				}
 			};
+#if DRAW_CONTENT
 
 			DrawContent += CharMap_DrawContent;
+#endif
 		}
-#if true
+
 		private void CharMap_DrawContent (Rect viewport)
 		{
 			//Rune ReplaceNonPrintables (Rune c)
@@ -125,10 +137,10 @@ namespace UICatalog {
 			//}
 
 			for (int header = 0; header < 16; header++) {
-				Move (viewport.X + RowHeaderWidth + (header * 2), 0);
+				Move (viewport.X + RowHeaderWidth + (header * H_SPACE), 0);
 				Driver.AddStr ($" {header:x} ");
 			}
-			for (int row = 0, y = 0; row < viewport.Height / 2 - 1; row++, y += 2) {
+			for (int row = 0, y = 0; row < viewport.Height / 2 - 1; row++, y+= V_SPACE) {
 				int val = (-viewport.Y + row) * 16;
 				if (val < MaxCodePointVal) {
 					var rowLabel = $"U+{val / 16:x4}x";
@@ -137,17 +149,17 @@ namespace UICatalog {
 					var prevColWasWide = false;
 					for (int col = 0; col < 16; col++) {
 						var rune = new Rune ((uint)((uint)(-viewport.Y + row) * 16 + col));
-						Move (viewport.X + RowHeaderWidth + (col * 2) + (prevColWasWide ? 0 : 1), 0 + y + 1);
+						Move (viewport.X + RowHeaderWidth + (col * H_SPACE) + (prevColWasWide ? 0 : 1), y + 1);
 						Driver.AddRune (rune);
-						//prevColWasWide = Rune.ColumnWidth(rune) > 1;
+						//prevColWasWide = Rune.ColumnWidth (rune) > 1;
 					}
 				}
 			}
 		}
-#else
+#if BASE_DRAW_CONTENT
 		public override void OnDrawContent (Rect viewport)
 		{
-			CharMap_DrawContent(this, viewport);
+			CharMap_DrawContent (viewport);
 			base.OnDrawContent (viewport);
 		}
 #endif