Sfoglia il codice sorgente

Merge pull request #1021 from BDisp/netdriver-unicode

Fixes #1020. NetDriver does not deal well with unicode characters yet.
Charlie Kindel 4 anni fa
parent
commit
62b5800c84

+ 3 - 1
Example/demo.cs

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

+ 5 - 3
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -20,6 +20,7 @@ namespace Terminal.Gui {
 		public override int Cols => Curses.Cols;
 		public override int Cols => Curses.Cols;
 		public override int Rows => Curses.Lines;
 		public override int Rows => Curses.Lines;
 		public override int Top => 0;
 		public override int Top => 0;
+		public override bool HeightAsBuffer { get; set; }
 
 
 		// Current row, and current col, tracked by Move/AddRune only
 		// Current row, and current col, tracked by Move/AddRune only
 		int ccol, crow;
 		int ccol, crow;
@@ -70,6 +71,7 @@ namespace Terminal.Gui {
 		public override void Refresh () {
 		public override void Refresh () {
 			Curses.refresh ();
 			Curses.refresh ();
 			if (Curses.CheckWinChange ()) {
 			if (Curses.CheckWinChange ()) {
+				Clip = new Rect (0, 0, Cols, Rows);
 				TerminalResized?.Invoke ();
 				TerminalResized?.Invoke ();
 			}
 			}
 		}
 		}
@@ -645,16 +647,16 @@ namespace Terminal.Gui {
 		}
 		}
 
 
 		Action<MouseEvent> mouseHandler;
 		Action<MouseEvent> mouseHandler;
-		MainLoop mainLoop;
 
 
 		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
 		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
 		{
 		{
 			// Note: Curses doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called
 			// Note: Curses doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called
 			Curses.timeout (0);
 			Curses.timeout (0);
 			this.mouseHandler = mouseHandler;
 			this.mouseHandler = mouseHandler;
-			this.mainLoop = mainLoop;
 
 
-			(mainLoop.Driver as UnixMainLoop).AddWatch (0, UnixMainLoop.Condition.PollIn, x => {
+			var mLoop = mainLoop.Driver as UnixMainLoop;
+
+			mLoop.AddWatch (0, UnixMainLoop.Condition.PollIn, x => {
 				ProcessInput (keyHandler, keyDownHandler, keyUpHandler, mouseHandler);
 				ProcessInput (keyHandler, keyDownHandler, keyUpHandler, mouseHandler);
 				return true;
 				return true;
 			});
 			});

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

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

+ 227 - 113
Terminal.Gui/ConsoleDrivers/NetDriver.cs

@@ -18,83 +18,59 @@ namespace Terminal.Gui {
 		public override int Cols => cols;
 		public override int Cols => cols;
 		public override int Rows => rows;
 		public override int Rows => rows;
 		public override int Top => top;
 		public override int Top => top;
+		public override bool HeightAsBuffer { get; set; }
 
 
 		// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
 		// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
 		int [,,] contents;
 		int [,,] contents;
 		bool [] dirtyLine;
 		bool [] dirtyLine;
 
 
-		public NetDriver ()
-		{
-			ResizeScreen ();
-			UpdateOffscreen ();
-		}
-
-		void UpdateOffscreen ()
-		{
-			int cols = Cols;
-			int rows = Rows;
-
-			contents = new int [rows, cols, 3];
-			dirtyLine = new bool [rows];
-			for (int row = 0; row < rows; row++) {
-				for (int c = 0; c < cols; c++) {
-					contents [row, c, 0] = ' ';
-					contents [row, c, 1] = (ushort)Colors.TopLevel.Normal;
-					contents [row, c, 2] = 0;
-					dirtyLine [row] = true;
-				}
-			}
-		}
-
 		static bool sync = false;
 		static bool sync = false;
 
 
-		bool needMove;
 		// Current row, and current col, tracked by Move/AddCh only
 		// Current row, and current col, tracked by Move/AddCh only
 		int ccol, crow;
 		int ccol, crow;
 		public override void Move (int col, int row)
 		public override void Move (int col, int row)
 		{
 		{
 			ccol = col;
 			ccol = col;
 			crow = row;
 			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)
 		public override void AddRune (Rune rune)
 		{
 		{
+			if (contents.Length != Rows * Cols * 3) {
+				return;
+			}
 			rune = MakePrintable (rune);
 			rune = MakePrintable (rune);
-			if (Clip.Contains (ccol, crow)) {
-				if (needMove) {
-					if (cols == Console.WindowWidth && rows == Console.WindowHeight) {
-						Console.SetCursorPosition (ccol, crow);
-					}
-					needMove = false;
-				}
+			var runeWidth = Rune.ColumnWidth (rune);
+			if (Clip.Contains (ccol, crow) && ccol + Math.Max (runeWidth, 1) <= Cols) {
 				contents [crow, ccol, 0] = (int)(uint)rune;
 				contents [crow, ccol, 0] = (int)(uint)rune;
 				contents [crow, ccol, 1] = currentAttribute;
 				contents [crow, ccol, 1] = currentAttribute;
 				contents [crow, ccol, 2] = 1;
 				contents [crow, ccol, 2] = 1;
 				dirtyLine [crow] = true;
 				dirtyLine [crow] = true;
-			} else
-				needMove = true;
-			ccol++;
+
+				ccol++;
+				if (runeWidth > 1) {
+					for (int i = 1; i < runeWidth; i++) {
+						if (ccol < cols) {
+							contents [crow, ccol, 2] = 0;
+						} else {
+							break;
+						}
+						ccol++;
+					}
+				}
+			} else if (ccol < cols && crow < rows) {
+				contents [crow, ccol, 2] = 1;
+				dirtyLine [crow] = true;
+			}
+
 			//if (ccol == Cols) {
 			//if (ccol == Cols) {
 			//	ccol = 0;
 			//	ccol = 0;
 			//	if (crow + 1 < Rows)
 			//	if (crow + 1 < Rows)
 			//		crow++;
 			//		crow++;
 			//}
 			//}
-			if (sync)
+			if (sync) {
 				UpdateScreen ();
 				UpdateScreen ();
+			}
 		}
 		}
 
 
 		public override void AddStr (ustring str)
 		public override void AddStr (ustring str)
@@ -122,10 +98,24 @@ namespace Terminal.Gui {
 			return new Attribute () { value = ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff) };
 			return new Attribute () { value = ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff) };
 		}
 		}
 
 
+		bool isWinPlatform;
+
 		public override void Init (Action terminalResized)
 		public override void Init (Action terminalResized)
 		{
 		{
 			TerminalResized = terminalResized;
 			TerminalResized = terminalResized;
+
 			Console.TreatControlCAsInput = true;
 			Console.TreatControlCAsInput = true;
+			var p = Environment.OSVersion.Platform;
+			if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
+				isWinPlatform = true;
+			}
+
+			cols = Console.WindowWidth;
+			rows = Console.WindowHeight;
+
+			Clear ();
+			ResizeScreen ();
+			UpdateOffScreen ();
 
 
 			Colors.TopLevel = new ColorScheme ();
 			Colors.TopLevel = new ColorScheme ();
 			Colors.Base = new ColorScheme ();
 			Colors.Base = new ColorScheme ();
@@ -163,15 +153,71 @@ namespace Terminal.Gui {
 			Colors.Error.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
 			Colors.Error.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
 			Colors.Error.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Red);
 			Colors.Error.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Red);
 			Colors.Error.HotFocus = Colors.Error.HotNormal;
 			Colors.Error.HotFocus = Colors.Error.HotNormal;
-			Clear ();
 		}
 		}
 
 
 		void ResizeScreen ()
 		void ResizeScreen ()
 		{
 		{
-			cols = Console.WindowWidth;
-			rows = Console.WindowHeight;
+			if (!HeightAsBuffer) {
+				if (Console.WindowHeight > 0) {
+					// Can raise an exception while is still resizing.
+					try {
+						// Not supported on Unix.
+						if (isWinPlatform) {
+#pragma warning disable CA1416
+							Console.CursorTop = 0;
+							Console.CursorLeft = 0;
+							Console.WindowTop = 0;
+							Console.WindowLeft = 0;
+							Console.SetBufferSize (Cols, Rows);
+#pragma warning restore CA1416
+						} else {
+							//Console.Out.Write ($"\x1b[8;{Console.WindowHeight};{Console.WindowWidth}t");
+							Console.Out.Write ($"\x1b[0;0" +
+								$";{Rows};{Cols}w");
+						}
+					} catch (System.IO.IOException) {
+						return;
+					} catch (ArgumentOutOfRangeException) {
+						return;
+					}
+				}
+			} else {
+				if (isWinPlatform && Console.WindowHeight > 0) {
+					// Can raise an exception while is still resizing.
+					try {
+#pragma warning disable CA1416
+						Console.WindowTop = Math.Max (Math.Min (top, Rows - Console.WindowHeight), 0);
+#pragma warning restore CA1416
+					} catch (Exception) {
+						return;
+					}
+				} else {
+					Console.Out.Write ($"\x1b[{top};{Console.WindowLeft}" +
+						$";{Rows};{Cols}w");
+				}
+			}
+
 			Clip = new Rect (0, 0, Cols, Rows);
 			Clip = new Rect (0, 0, Cols, Rows);
-			top = Console.WindowTop;
+
+			contents = new int [Rows, Cols, 3];
+			dirtyLine = new bool [Rows];
+		}
+
+		void UpdateOffScreen ()
+		{
+			// Can raise an exception while is still resizing.
+			try {
+				for (int row = 0; row < rows; row++) {
+					for (int c = 0; c < cols; c++) {
+						contents [row, c, 0] = ' ';
+						contents [row, c, 1] = (ushort)Colors.TopLevel.Normal;
+						contents [row, c, 2] = 0;
+						dirtyLine [row] = true;
+					}
+				}
+			} catch (IndexOutOfRangeException) { }
+
+			winChanging = false;
 		}
 		}
 
 
 		public override Attribute MakeAttribute (Color fore, Color back)
 		public override Attribute MakeAttribute (Color fore, Color back)
@@ -196,29 +242,42 @@ namespace Terminal.Gui {
 
 
 		public override void UpdateScreen ()
 		public override void UpdateScreen ()
 		{
 		{
-			if (Rows == 0) {
+			if (winChanging || Console.WindowHeight == 0 || contents.Length != Rows * Cols * 3
+				|| (!HeightAsBuffer && Rows != Console.WindowHeight)
+				|| (HeightAsBuffer && Rows != Console.BufferHeight)) {
 				return;
 				return;
 			}
 			}
 
 
-			int rows = Rows;
+			int top = Top;
+			int rows = Math.Min (Console.WindowHeight + top, Rows);
 			int cols = Cols;
 			int cols = Cols;
 
 
 			for (int row = top; row < rows; row++) {
 			for (int row = top; row < rows; row++) {
-				if (!dirtyLine [row])
+				if (!dirtyLine [row]) {
 					continue;
 					continue;
+				}
 				dirtyLine [row] = false;
 				dirtyLine [row] = false;
 				for (int col = 0; col < cols; col++) {
 				for (int col = 0; col < cols; col++) {
-					if (contents [row, col, 2] != 1)
+					if (contents [row, col, 2] != 1) {
 						continue;
 						continue;
-
+					}
 					if (Console.WindowHeight > 0) {
 					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++) {
 					for (; col < cols && contents [row, col, 2] == 1; col++) {
+						// Needed for the .Net Framework.
+						if (row == rows - 1 && col == cols - 1) {
+							break;
+						}
 						var color = contents [row, col, 1];
 						var color = contents [row, col, 1];
-						if (color != redrawColor)
+						if (color != redrawColor) {
 							SetColor (color);
 							SetColor (color);
-
+						}
 						Console.Write ((char)contents [row, col, 0]);
 						Console.Write ((char)contents [row, col, 0]);
 						contents [row, col, 2] = 0;
 						contents [row, col, 2] = 0;
 					}
 					}
@@ -230,12 +289,6 @@ namespace Terminal.Gui {
 
 
 		public override void Refresh ()
 		public override void Refresh ()
 		{
 		{
-			if (Console.WindowWidth != Cols || Console.WindowHeight != Rows || Console.WindowTop != Top) {
-				ResizeScreen ();
-				UpdateOffscreen ();
-				TerminalResized.Invoke ();
-			}
-
 			UpdateScreen ();
 			UpdateScreen ();
 		}
 		}
 
 
@@ -243,10 +296,12 @@ namespace Terminal.Gui {
 		{
 		{
 			// Prevents the exception of size changing during resizing.
 			// Prevents the exception of size changing during resizing.
 			try {
 			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);
 					Console.SetCursorPosition (ccol, crow);
 				}
 				}
-			} catch (ArgumentOutOfRangeException) { }
+			} catch (System.IO.IOException) {
+			} catch (ArgumentOutOfRangeException) {
+			}
 		}
 		}
 
 
 		public override void StartReportingMouseMoves ()
 		public override void StartReportingMouseMoves ()
@@ -269,36 +324,39 @@ namespace Terminal.Gui {
 
 
 		Key MapKey (ConsoleKeyInfo keyInfo)
 		Key MapKey (ConsoleKeyInfo keyInfo)
 		{
 		{
-			MapKeyModifiers (keyInfo);
+			MapKeyModifiers (keyInfo, (Key)keyInfo.Key);
 			switch (keyInfo.Key) {
 			switch (keyInfo.Key) {
 			case ConsoleKey.Escape:
 			case ConsoleKey.Escape:
-				return Key.Esc;
+				return MapKeyModifiers (keyInfo, Key.Esc);
 			case ConsoleKey.Tab:
 			case ConsoleKey.Tab:
 				return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab;
 				return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab;
 			case ConsoleKey.Home:
 			case ConsoleKey.Home:
-				return Key.Home;
+				return MapKeyModifiers (keyInfo, Key.Home);
 			case ConsoleKey.End:
 			case ConsoleKey.End:
-				return Key.End;
+				return MapKeyModifiers (keyInfo, Key.End);
 			case ConsoleKey.LeftArrow:
 			case ConsoleKey.LeftArrow:
-				return Key.CursorLeft;
+				return MapKeyModifiers (keyInfo, Key.CursorLeft);
 			case ConsoleKey.RightArrow:
 			case ConsoleKey.RightArrow:
-				return Key.CursorRight;
+				return MapKeyModifiers (keyInfo, Key.CursorRight);
 			case ConsoleKey.UpArrow:
 			case ConsoleKey.UpArrow:
-				return Key.CursorUp;
+				return MapKeyModifiers (keyInfo, Key.CursorUp);
 			case ConsoleKey.DownArrow:
 			case ConsoleKey.DownArrow:
-				return Key.CursorDown;
+				return MapKeyModifiers (keyInfo, Key.CursorDown);
 			case ConsoleKey.PageUp:
 			case ConsoleKey.PageUp:
-				return Key.PageUp;
+				return MapKeyModifiers (keyInfo, Key.PageUp);
 			case ConsoleKey.PageDown:
 			case ConsoleKey.PageDown:
-				return Key.PageDown;
+				return MapKeyModifiers (keyInfo, Key.PageDown);
 			case ConsoleKey.Enter:
 			case ConsoleKey.Enter:
-				return Key.Enter;
+				return MapKeyModifiers (keyInfo, Key.Enter);
 			case ConsoleKey.Spacebar:
 			case ConsoleKey.Spacebar:
-				return Key.Space;
+				return MapKeyModifiers (keyInfo, Key.Space);
 			case ConsoleKey.Backspace:
 			case ConsoleKey.Backspace:
-				return Key.Backspace;
+				return MapKeyModifiers (keyInfo, Key.Backspace);
 			case ConsoleKey.Delete:
 			case ConsoleKey.Delete:
-				return Key.Delete;
+				return MapKeyModifiers (keyInfo, Key.DeleteChar);
+			case ConsoleKey.Insert:
+				return MapKeyModifiers (keyInfo, Key.InsertChar);
+
 
 
 			case ConsoleKey.Oem1:
 			case ConsoleKey.Oem1:
 			case ConsoleKey.Oem2:
 			case ConsoleKey.Oem2:
@@ -327,7 +385,7 @@ namespace Terminal.Gui {
 				}
 				}
 				if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
 				if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
 					if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) {
 					if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) {
-						return (Key)((uint)Key.A + delta);
+						return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta));
 					}
 					}
 				}
 				}
 				return (Key)((uint)keyInfo.KeyChar);
 				return (Key)((uint)keyInfo.KeyChar);
@@ -342,7 +400,7 @@ namespace Terminal.Gui {
 				}
 				}
 				if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
 				if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
 					if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30) {
 					if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30) {
-						return (Key)((uint)Key.D0 + delta);
+						return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
 					}
 					}
 				}
 				}
 				return (Key)((uint)keyInfo.KeyChar);
 				return (Key)((uint)keyInfo.KeyChar);
@@ -350,7 +408,7 @@ namespace Terminal.Gui {
 			if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) {
 			if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) {
 				var delta = key - ConsoleKey.F1;
 				var delta = key - ConsoleKey.F1;
 				if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
 				if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
-					return (Key)((uint)Key.F1 + delta);
+					return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta));
 				}
 				}
 
 
 				return (Key)((uint)Key.F1 + delta);
 				return (Key)((uint)Key.F1 + delta);
@@ -364,41 +422,82 @@ namespace Terminal.Gui {
 
 
 		KeyModifiers keyModifiers;
 		KeyModifiers keyModifiers;
 
 
-		void MapKeyModifiers (ConsoleKeyInfo keyInfo)
+		Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
 		{
 		{
-			if (keyModifiers == null)
+			if (keyModifiers == null) {
 				keyModifiers = new KeyModifiers ();
 				keyModifiers = new KeyModifiers ();
-
-			if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0)
+			}
+			Key keyMod = new Key ();
+			if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) {
+				keyMod = Key.ShiftMask;
 				keyModifiers.Shift = true;
 				keyModifiers.Shift = true;
-			if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0)
+			}
+			if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) {
+				keyMod |= Key.CtrlMask;
 				keyModifiers.Ctrl = true;
 				keyModifiers.Ctrl = true;
-			if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0)
+			}
+			if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) {
+				keyMod |= Key.AltMask;
 				keyModifiers.Alt = true;
 				keyModifiers.Alt = true;
+			}
+
+			return keyMod != Key.Null ? keyMod | key : key;
 		}
 		}
 
 
+		bool winChanging;
 		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
 		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
 		{
 		{
+			var mLoop = mainLoop.Driver as NetMainLoop;
+
 			// Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called
 			// 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) {
+			mLoop.KeyPressed = (consoleKey) => {
 				var map = MapKey (consoleKey);
 				var map = MapKey (consoleKey);
 				if (map == (Key)0xffffffff) {
 				if (map == (Key)0xffffffff) {
 					return;
 					return;
 				}
 				}
-				keyHandler (new KeyEvent (map, keyModifiers));
-				keyUpHandler (new KeyEvent (map, keyModifiers));
-				keyModifiers = null;
+
+				if (map == (Key.Space | Key.CtrlMask) || map == (Key.Space | Key.AltMask)) {
+					map = Key.AltMask;
+					keyModifiers.Alt = true;
+					keyModifiers.Ctrl = false;
+					keyDownHandler (new KeyEvent (map, keyModifiers));
+					keyUpHandler (new KeyEvent (map, keyModifiers));
+				} else {
+					keyDownHandler (new KeyEvent (map, keyModifiers));
+					keyHandler (new KeyEvent (map, keyModifiers));
+					keyUpHandler (new KeyEvent (map, keyModifiers));
+				}
+				keyModifiers = new KeyModifiers ();
+			};
+
+			mLoop.WinChanged = (e) => {
+				winChanging = true;
+				const int Min_WindowWidth = 14;
+				Size size = new Size ();
+				if (!HeightAsBuffer) {
+					size = new Size (Math.Max (Min_WindowWidth, Console.WindowWidth),
+						Console.WindowHeight);
+					top = 0;
+				} else {
+					size = new Size (Console.BufferWidth, Console.BufferHeight);
+					top = e;
+				}
+				cols = size.Width;
+				rows = size.Height;
+				ResizeScreen ();
+				UpdateOffScreen ();
+				if (!winChanging) {
+					TerminalResized.Invoke ();
+				}
 			};
 			};
 		}
 		}
 
 
 		public override void SetColors (ConsoleColor foreground, ConsoleColor background)
 		public override void SetColors (ConsoleColor foreground, ConsoleColor background)
 		{
 		{
-			throw new NotImplementedException ();
 		}
 		}
 
 
 		public override void SetColors (short foregroundColorId, short backgroundColorId)
 		public override void SetColors (short foregroundColorId, short backgroundColorId)
 		{
 		{
-			throw new NotImplementedException ();
 		}
 		}
 
 
 		public override void CookMouse ()
 		public override void CookMouse ()
@@ -424,14 +523,15 @@ namespace Terminal.Gui {
 	/// <remarks>
 	/// <remarks>
 	/// This implementation is used for NetDriver.
 	/// This implementation is used for NetDriver.
 	/// </remarks>
 	/// </remarks>
-	public class NetMainLoop : IMainLoopDriver {
+	internal class NetMainLoop : IMainLoopDriver {
 		ManualResetEventSlim keyReady = new ManualResetEventSlim (false);
 		ManualResetEventSlim keyReady = new ManualResetEventSlim (false);
 		ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
 		ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
 		ManualResetEventSlim winChange = new ManualResetEventSlim (false);
 		ManualResetEventSlim winChange = new ManualResetEventSlim (false);
-		ConsoleKeyInfo? keyResult = null;
+		Queue<ConsoleKeyInfo?> keyResult = new Queue<ConsoleKeyInfo?> ();
 		MainLoop mainLoop;
 		MainLoop mainLoop;
 		ConsoleDriver consoleDriver;
 		ConsoleDriver consoleDriver;
 		bool winChanged;
 		bool winChanged;
+		int newTop;
 		CancellationTokenSource tokenSource = new CancellationTokenSource ();
 		CancellationTokenSource tokenSource = new CancellationTokenSource ();
 
 
 		/// <summary>
 		/// <summary>
@@ -439,6 +539,11 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// </summary>
 		public Action<ConsoleKeyInfo> KeyPressed;
 		public Action<ConsoleKeyInfo> KeyPressed;
 
 
+		/// <summary>
+		/// Invoked when the window is changed.
+		/// </summary>
+		public Action<int> WinChanged;
+
 		/// <summary>
 		/// <summary>
 		/// Initializes the class with the console driver.
 		/// Initializes the class with the console driver.
 		/// </summary>
 		/// </summary>
@@ -449,7 +554,7 @@ namespace Terminal.Gui {
 		public NetMainLoop (ConsoleDriver consoleDriver = null)
 		public NetMainLoop (ConsoleDriver consoleDriver = null)
 		{
 		{
 			if (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;
 			this.consoleDriver = consoleDriver;
 		}
 		}
@@ -459,7 +564,9 @@ namespace Terminal.Gui {
 			while (true) {
 			while (true) {
 				waitForProbe.Wait ();
 				waitForProbe.Wait ();
 				waitForProbe.Reset ();
 				waitForProbe.Reset ();
-				keyResult = Console.ReadKey (true);
+				if (keyResult.Count == 0) {
+					keyResult.Enqueue (Console.ReadKey (true));
+				}
 				keyReady.Set ();
 				keyReady.Set ();
 			}
 			}
 		}
 		}
@@ -475,12 +582,23 @@ namespace Terminal.Gui {
 			}
 			}
 		}
 		}
 
 
+		int lastWindowHeight;
 		void WaitWinChange ()
 		void WaitWinChange ()
 		{
 		{
 			while (true) {
 			while (true) {
-				if (Console.WindowWidth != consoleDriver.Cols || Console.WindowHeight != consoleDriver.Rows
-					|| Console.WindowTop != consoleDriver.Top) { // Top only working on Windows.
-					return;
+				if (!consoleDriver.HeightAsBuffer) {
+					if (Console.WindowWidth != consoleDriver.Cols || Console.WindowHeight != consoleDriver.Rows) {
+						return;
+					}
+				} else {
+					if (Console.BufferWidth != consoleDriver.Cols || Console.BufferHeight != consoleDriver.Rows
+						|| Console.WindowTop != consoleDriver.Top
+						|| Console.WindowHeight != lastWindowHeight) {
+						// Top only working on Windows.
+						newTop = Console.WindowTop;
+						lastWindowHeight = Console.WindowHeight;
+						return;
+					}
 				}
 				}
 			}
 			}
 		}
 		}
@@ -499,8 +617,6 @@ namespace Terminal.Gui {
 
 
 		bool IMainLoopDriver.EventsPending (bool wait)
 		bool IMainLoopDriver.EventsPending (bool wait)
 		{
 		{
-			long now = DateTime.UtcNow.Ticks;
-
 			waitForProbe.Set ();
 			waitForProbe.Set ();
 			winChange.Set ();
 			winChange.Set ();
 
 
@@ -519,7 +635,7 @@ namespace Terminal.Gui {
 			}
 			}
 
 
 			if (!tokenSource.IsCancellationRequested) {
 			if (!tokenSource.IsCancellationRequested) {
-				return keyResult.HasValue || CheckTimers (wait, out _) || winChanged;
+				return keyResult.Count > 0 || CheckTimers (wait, out _) || winChanged;
 			}
 			}
 
 
 			tokenSource.Dispose ();
 			tokenSource.Dispose ();
@@ -552,14 +668,12 @@ namespace Terminal.Gui {
 
 
 		void IMainLoopDriver.MainIteration ()
 		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) {
 			if (winChanged) {
 				winChanged = false;
 				winChanged = false;
-				consoleDriver.Refresh ();
+				WinChanged?.Invoke (newTop);
 			}
 			}
 		}
 		}
 	}
 	}

+ 385 - 234
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -86,6 +86,32 @@ namespace Terminal.Gui {
 			return WriteConsoleOutput (ScreenBuffer, charInfoBuffer, coords, new Coord () { X = window.Left, Y = window.Top }, ref window);
 			return WriteConsoleOutput (ScreenBuffer, charInfoBuffer, coords, new Coord () { X = window.Left, Y = window.Top }, ref window);
 		}
 		}
 
 
+		public void ReadFromConsoleOutput (Size size, Coord coords)
+		{
+			ScreenBuffer = CreateConsoleScreenBuffer (
+				DesiredAccess.GenericRead | DesiredAccess.GenericWrite,
+				ShareMode.FileShareRead | ShareMode.FileShareWrite,
+				IntPtr.Zero,
+				1,
+				IntPtr.Zero
+			);
+			if (ScreenBuffer == INVALID_HANDLE_VALUE) {
+				var err = Marshal.GetLastWin32Error ();
+
+				if (err != 0)
+					throw new System.ComponentModel.Win32Exception (err);
+			}
+
+			if (!SetConsoleActiveScreenBuffer (ScreenBuffer)) {
+				var err = Marshal.GetLastWin32Error ();
+				throw new System.ComponentModel.Win32Exception (err);
+			}
+
+			OriginalStdOutChars = new CharInfo [size.Height * size.Width];
+			SmallRect window = new SmallRect ();
+			ReadConsoleOutput (OutputHandle, OriginalStdOutChars, coords, new Coord () { X = 0, Y = 0 }, ref window);
+		}
+
 		public bool SetCursorPosition (Coord position)
 		public bool SetCursorPosition (Coord position)
 		{
 		{
 			return SetConsoleCursorPosition (ScreenBuffer, position);
 			return SetConsoleCursorPosition (ScreenBuffer, position);
@@ -207,7 +233,7 @@ namespace Terminal.Gui {
 			public override string ToString () => $"({X},{Y})";
 			public override string ToString () => $"({X},{Y})";
 		};
 		};
 
 
-		internal struct WindowBufferSizeRecord {
+		public struct WindowBufferSizeRecord {
 			public Coordinate size;
 			public Coordinate size;
 
 
 			public WindowBufferSizeRecord (short x, short y)
 			public WindowBufferSizeRecord (short x, short y)
@@ -355,6 +381,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)]
 		[DllImport ("kernel32.dll", SetLastError = true)]
 		static extern IntPtr GetStdHandle (int nStdHandle);
 		static extern IntPtr GetStdHandle (int nStdHandle);
 
 
@@ -431,252 +471,158 @@ namespace Terminal.Gui {
 
 
 				return numberEventsRead == 0
 				return numberEventsRead == 0
 					? null
 					? null
-					: new [] {Marshal.PtrToStructure<InputRecord> (pRecord)};
+					: new [] { Marshal.PtrToStructure<InputRecord> (pRecord) };
 			} catch (Exception) {
 			} catch (Exception) {
 				return null;
 				return null;
 			} finally {
 			} finally {
 				Marshal.FreeHGlobal (pRecord);
 				Marshal.FreeHGlobal (pRecord);
 			}
 			}
 		}
 		}
-#if false	// Not needed on the constructor. Perhaps could be used on resizing. To study.
+
+		// Not needed on the constructor. Perhaps could be used on resizing. To study.
 		[DllImport ("kernel32.dll", ExactSpelling = true)]
 		[DllImport ("kernel32.dll", ExactSpelling = true)]
-		private static extern IntPtr GetConsoleWindow ();
+		static extern IntPtr GetConsoleWindow ();
+
+		[DllImport ("user32.dll")]
+		[return: MarshalAs (UnmanagedType.Bool)]
+		static extern bool GetWindowPlacement (IntPtr hWnd, ref WindowPlacement lpwndpl);
+
+		[DllImport ("user32.dll", SetLastError = true)]
+		[return: MarshalAs (UnmanagedType.Bool)]
+		static extern bool SetWindowPlacement (IntPtr hWnd, [In] ref WindowPlacement lpwndpl);
+
+		internal struct WindowPlacement {
+			public int length;
+			public int flags;
+			public int showCmd;
+			public System.Drawing.Point ptMinPosition;
+			public System.Drawing.Point ptMaxPosition;
+			public System.Drawing.Rectangle rcNormalPosition;
+			public System.Drawing.Rectangle rcDevice;
+		}
 
 
-		[DllImport ("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
-		private static extern bool ShowWindow (IntPtr hWnd, int nCmdShow);
+		// flags
+		public const int WPF_SET_MIN_POSITION = 1;
+		public const int WPF_RESTORE_TO_MAXIMIZED = 2;
+		public const int WPF_ASYNC_WINDOWPLACEMENT = 4;
 
 
+		// showCmd
 		public const int HIDE = 0;
 		public const int HIDE = 0;
-		public const int MAXIMIZE = 3;
+		public const int NORMAL = 1;
+		public const int SHOW_MINIMIZED = 2;
+		public const int SHOW_MAXIMIZED = 3;
+		public const int SHOW_NOACTIVATE = 4;
+		public const int SHOW = 5;
 		public const int MINIMIZE = 6;
 		public const int MINIMIZE = 6;
+		public const int SHOW_MIN_NOACTIVE = 7;
+		public const int SHOW_NA = 8;
 		public const int RESTORE = 9;
 		public const int RESTORE = 9;
+		public const int SHOW_DEFAULT = 10;
+		public const int FORCE_MINIMIZE = 11;
 
 
-		internal void ShowWindow (int state)
+		internal WindowPlacement GetWindow ()
 		{
 		{
 			IntPtr thisConsole = GetConsoleWindow ();
 			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 {
-			public short Left;
-			public short Top;
-			public short Right;
-			public short Bottom;
+			WindowPlacement placement = new WindowPlacement {
+				length = Marshal.SizeOf (typeof (WindowPlacement))
+			};
+			GetWindowPlacement (thisConsole, ref placement);
 
 
-			public SMALL_RECT (short Left, short Top, short Right, short Bottom)
-			{
-				this.Left = Left;
-				this.Top = Top;
-				this.Right = Right;
-				this.Bottom = Bottom;
-			}
+			return placement;
 		}
 		}
 
 
-		[StructLayout (LayoutKind.Sequential)]
-		public struct CONSOLE_SCREEN_BUFFER_INFO {
-			public int dwSize;
-			public int dwCursorPosition;
-			public short wAttributes;
-			public SMALL_RECT srWindow;
-			public int dwMaximumWindowSize;
+		internal void SetWindow (int showCmd)
+		{
+			IntPtr thisConsole = GetConsoleWindow ();
+			WindowPlacement placement = new WindowPlacement {
+				length = Marshal.SizeOf (typeof (WindowPlacement)),
+				showCmd = showCmd
+			};
+			SetWindowPlacement (thisConsole, ref placement);
 		}
 		}
 
 
+#if false
 		[DllImport ("kernel32.dll", SetLastError = true)]
 		[DllImport ("kernel32.dll", SetLastError = true)]
-		static extern bool GetConsoleScreenBufferInfo (IntPtr hConsoleOutput, out CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo);
+		static extern bool GetConsoleScreenBufferInfo (IntPtr hConsoleOutput, out ConsoleScreenBufferInfo ConsoleScreenBufferInfo);
 
 
-		// Theoretically GetConsoleScreenBuffer height should give the console Windoww size
+		// Theoretically GetConsoleScreenBuffer height should give the console Window size, but the Top is always 0.
 		// It does not work, however, and always returns the size the window was initially created at
 		// It does not work, however, and always returns the size the window was initially created at
-		internal Size GetWindowSize ()
+		internal Size GetWindowSize (IntPtr handle)
 		{
 		{
-			var consoleScreenBufferInfo = new CONSOLE_SCREEN_BUFFER_INFO ();
-			//consoleScreenBufferInfo.dwSize = Marshal.SizeOf (typeof (CONSOLE_SCREEN_BUFFER_INFO));
-			GetConsoleScreenBufferInfo (OutputHandle, out consoleScreenBufferInfo);
-			return new Size (consoleScreenBufferInfo.srWindow.Right - consoleScreenBufferInfo.srWindow.Left,
-				consoleScreenBufferInfo.srWindow.Bottom - consoleScreenBufferInfo.srWindow.Top);
+			GetConsoleScreenBufferInfo (handle, out ConsoleScreenBufferInfo consoleScreenBufferInfo);
+			return new Size (consoleScreenBufferInfo.srWindow.Right - consoleScreenBufferInfo.srWindow.Left + 1,
+				consoleScreenBufferInfo.srWindow.Bottom - consoleScreenBufferInfo.srWindow.Top + 1);
 		}
 		}
 #endif
 #endif
 	}
 	}
 
 
-	internal class WindowsDriver : ConsoleDriver, IMainLoopDriver {
+	internal class WindowsDriver : ConsoleDriver {
 		static bool sync = false;
 		static bool sync = false;
-		ManualResetEventSlim eventReady = new ManualResetEventSlim (false);
-		ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
-		MainLoop mainLoop;
 		WindowsConsole.CharInfo [] OutputBuffer;
 		WindowsConsole.CharInfo [] OutputBuffer;
-		int cols, rows;
+		int cols, rows, top;
 		WindowsConsole winConsole;
 		WindowsConsole winConsole;
 		WindowsConsole.SmallRect damageRegion;
 		WindowsConsole.SmallRect damageRegion;
 
 
 		public override int Cols => cols;
 		public override int Cols => cols;
 		public override int Rows => rows;
 		public override int Rows => rows;
-		public override int Top => 0;
-
-		public WindowsDriver ()
-		{
-			winConsole = new WindowsConsole ();
-
-			SetupColorsAndBorders ();
-
-			cols = Console.WindowWidth;
-			rows = Console.WindowHeight;
-#if false
-			winConsole.ShowWindow (WindowsConsole.RESTORE);
-#endif
-			WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
-
-			ResizeScreen ();
-			UpdateOffScreen ();
-
-			Task.Run ((Action)WindowsInputHandler);
-		}
-
-		private void SetupColorsAndBorders ()
-		{
-			Colors.TopLevel = new ColorScheme ();
-			Colors.Base = new ColorScheme ();
-			Colors.Dialog = new ColorScheme ();
-			Colors.Menu = new ColorScheme ();
-			Colors.Error = new ColorScheme ();
-
-			Colors.TopLevel.Normal = MakeColor (ConsoleColor.Green, ConsoleColor.Black);
-			Colors.TopLevel.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkCyan);
-			Colors.TopLevel.HotNormal = MakeColor (ConsoleColor.DarkYellow, ConsoleColor.Black);
-			Colors.TopLevel.HotFocus = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.DarkCyan);
-
-			Colors.Base.Normal = MakeColor (ConsoleColor.White, ConsoleColor.DarkBlue);
-			Colors.Base.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
-			Colors.Base.HotNormal = MakeColor (ConsoleColor.DarkCyan, ConsoleColor.DarkBlue);
-			Colors.Base.HotFocus = MakeColor (ConsoleColor.Blue, ConsoleColor.Gray);
-
-			Colors.Menu.Normal = MakeColor (ConsoleColor.White, ConsoleColor.DarkGray);
-			Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
-			Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.DarkGray);
-			Colors.Menu.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Black);
-			Colors.Menu.Disabled = MakeColor (ConsoleColor.Gray, ConsoleColor.DarkGray);
-
-			Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
-			Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.DarkGray);
-			Colors.Dialog.HotNormal = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.Gray);
-			Colors.Dialog.HotFocus = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.DarkGray);
-
-			Colors.Error.Normal = MakeColor (ConsoleColor.DarkRed, ConsoleColor.White);
-			Colors.Error.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkRed);
-			Colors.Error.HotNormal = MakeColor (ConsoleColor.Black, ConsoleColor.White);
-			Colors.Error.HotFocus = MakeColor (ConsoleColor.Black, ConsoleColor.DarkRed);
-		}
-
-		[StructLayout (LayoutKind.Sequential)]
-		public struct ConsoleKeyInfoEx {
-			public ConsoleKeyInfo consoleKeyInfo;
-			public bool CapsLock;
-			public bool NumLock;
+		public override int Top => top;
+		public override bool HeightAsBuffer { get; set; }
 
 
-			public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock)
-			{
-				this.consoleKeyInfo = consoleKeyInfo;
-				CapsLock = capslock;
-				NumLock = numlock;
-			}
+		public WindowsConsole WinConsole {
+			get => winConsole;
+			private set => winConsole = value;
 		}
 		}
 
 
-		// 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 ();
-			}
-		}
+		Action<KeyEvent> keyHandler;
+		Action<KeyEvent> keyDownHandler;
+		Action<KeyEvent> keyUpHandler;
+		Action<MouseEvent> mouseHandler;
 
 
-		void IMainLoopDriver.Setup (MainLoop mainLoop)
+		public WindowsDriver ()
 		{
 		{
-			this.mainLoop = mainLoop;
+			winConsole = new WindowsConsole ();
 		}
 		}
 
 
-		void IMainLoopDriver.Wakeup ()
-		{
-			//tokenSource.Cancel ();
-			eventReady.Reset ();
-			eventReady.Set ();
-		}
+		bool winChanging;
 
 
-		bool IMainLoopDriver.EventsPending (bool wait)
+		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
 		{
 		{
-			if (CheckTimers (wait, out var waitTimeout)) {
-				return true;
-			}
-
-			result = null;
-			waitForProbe.Set ();
+			this.keyHandler = keyHandler;
+			this.keyDownHandler = keyDownHandler;
+			this.keyUpHandler = keyUpHandler;
+			this.mouseHandler = mouseHandler;
 
 
-			try {
-				if (!tokenSource.IsCancellationRequested) {
-					eventReady.Wait (waitTimeout, tokenSource.Token);
-				}
-			} catch (OperationCanceledException) {
-				return true;
-			} finally {
-				eventReady.Reset ();
-			}
+			var mLoop = mainLoop.Driver as WindowsMainLoop;
 
 
-			if (!tokenSource.IsCancellationRequested) {
-				return result != null || CheckTimers (wait, out waitTimeout);
-			}
+			mLoop.ProcessInput = (e) => ProcessInput (e);
 
 
-			tokenSource.Dispose ();
-			tokenSource = new CancellationTokenSource ();
-			return true;
+			mLoop.WinChanged = (e) => ChangeWin (e);
 		}
 		}
 
 
-		bool CheckTimers (bool wait, out int waitTimeout)
+		void ChangeWin (Size size)
 		{
 		{
-			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;
+			if (!HeightAsBuffer) {
+				winChanging = true;
+				top = 0;
+				cols = size.Width;
+				rows = size.Height;
+				var bufferCoords = new WindowsConsole.Coord () {
+					X = (short)cols,
+					Y = (short)rows
+				};
+				winConsole.ReadFromConsoleOutput (size, bufferCoords);
+				ResizeScreen ();
+				UpdateOffScreen ();
+				if (!winChanging) {
+					TerminalResized.Invoke ();
+				} else {
+					System.Diagnostics.Debugger.Break ();
+				}
 			}
 			}
-
-			return ic > 0;
 		}
 		}
 
 
-		Action<KeyEvent> keyHandler;
-		Action<KeyEvent> keyDownHandler;
-		Action<KeyEvent> keyUpHandler;
-		Action<MouseEvent> mouseHandler;
-
-		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
+		void ProcessInput (WindowsConsole.InputRecord inputEvent)
 		{
 		{
-			this.keyHandler = keyHandler;
-			this.keyDownHandler = keyDownHandler;
-			this.keyUpHandler = keyUpHandler;
-			this.mouseHandler = mouseHandler;
-		}
-
-		void IMainLoopDriver.MainIteration ()
-		{
-			if (result == null)
-				return;
-
-			var inputEvent = result [0];
 			switch (inputEvent.EventType) {
 			switch (inputEvent.EventType) {
 			case WindowsConsole.EventType.Key:
 			case WindowsConsole.EventType.Key:
 				var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent));
 				var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent));
@@ -766,20 +712,18 @@ namespace Terminal.Gui {
 				break;
 				break;
 
 
 			case WindowsConsole.EventType.WindowBufferSize:
 			case WindowsConsole.EventType.WindowBufferSize:
-				cols = inputEvent.WindowBufferSizeEvent.size.X;
-				rows = inputEvent.WindowBufferSizeEvent.size.Y;
-				ResizeScreen ();
-				UpdateOffScreen ();
-				TerminalResized?.Invoke ();
+				if (HeightAsBuffer) {
+					cols = inputEvent.WindowBufferSizeEvent.size.X;
+					rows = inputEvent.WindowBufferSizeEvent.size.Y;
+					ResizeScreen ();
+					UpdateOffScreen ();
+					TerminalResized?.Invoke ();
+				}
 				break;
 				break;
 
 
 			case WindowsConsole.EventType.Focus:
 			case WindowsConsole.EventType.Focus:
 				break;
 				break;
-
-			default:
-				break;
 			}
 			}
-			result = null;
 		}
 		}
 
 
 		WindowsConsole.ButtonState? LastMouseButtonPressed = null;
 		WindowsConsole.ButtonState? LastMouseButtonPressed = null;
@@ -871,23 +815,23 @@ namespace Terminal.Gui {
 					Y = mouseEvent.MousePosition.Y
 					Y = mouseEvent.MousePosition.Y
 				};
 				};
 				//if (p == point) {
 				//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 {
 				//} else {
 				//	mouseFlag = 0;
 				//	mouseFlag = 0;
 				//}
 				//}
@@ -1009,7 +953,7 @@ namespace Terminal.Gui {
 
 
 		KeyModifiers keyModifiers;
 		KeyModifiers keyModifiers;
 
 
-		public ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEventRecord keyEvent)
+		public WindowsConsole.ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEventRecord keyEvent)
 		{
 		{
 			var state = keyEvent.dwControlKeyState;
 			var state = keyEvent.dwControlKeyState;
 
 
@@ -1036,10 +980,10 @@ namespace Terminal.Gui {
 				keyModifiers.Scrolllock = scrolllock;
 				keyModifiers.Scrolllock = scrolllock;
 
 
 			var ConsoleKeyInfo = new ConsoleKeyInfo (keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control);
 			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;
 			var keyInfo = keyInfoEx.consoleKeyInfo;
 			switch (keyInfo.Key) {
 			switch (keyInfo.Key) {
@@ -1163,7 +1107,7 @@ namespace Terminal.Gui {
 			return (Key)(0xffffffff);
 			return (Key)(0xffffffff);
 		}
 		}
 
 
-		private Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
+		Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
 		{
 		{
 			Key keyMod = new Key ();
 			Key keyMod = new Key ();
 			if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0)
 			if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0)
@@ -1179,7 +1123,48 @@ namespace Terminal.Gui {
 		public override void Init (Action terminalResized)
 		public override void Init (Action terminalResized)
 		{
 		{
 			TerminalResized = terminalResized;
 			TerminalResized = terminalResized;
-			SetupColorsAndBorders ();
+
+			cols = Console.WindowWidth;
+			rows = Console.WindowHeight;
+#if false
+			winConsole.ShowWindow (WindowsConsole.RESTORE);
+#endif
+			WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
+
+			ResizeScreen ();
+			UpdateOffScreen ();
+
+			Colors.TopLevel = new ColorScheme ();
+			Colors.Base = new ColorScheme ();
+			Colors.Dialog = new ColorScheme ();
+			Colors.Menu = new ColorScheme ();
+			Colors.Error = new ColorScheme ();
+
+			Colors.TopLevel.Normal = MakeColor (ConsoleColor.Green, ConsoleColor.Black);
+			Colors.TopLevel.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkCyan);
+			Colors.TopLevel.HotNormal = MakeColor (ConsoleColor.DarkYellow, ConsoleColor.Black);
+			Colors.TopLevel.HotFocus = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.DarkCyan);
+
+			Colors.Base.Normal = MakeColor (ConsoleColor.White, ConsoleColor.DarkBlue);
+			Colors.Base.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
+			Colors.Base.HotNormal = MakeColor (ConsoleColor.DarkCyan, ConsoleColor.DarkBlue);
+			Colors.Base.HotFocus = MakeColor (ConsoleColor.Blue, ConsoleColor.Gray);
+
+			Colors.Menu.Normal = MakeColor (ConsoleColor.White, ConsoleColor.DarkGray);
+			Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
+			Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.DarkGray);
+			Colors.Menu.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Black);
+			Colors.Menu.Disabled = MakeColor (ConsoleColor.Gray, ConsoleColor.DarkGray);
+
+			Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
+			Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.DarkGray);
+			Colors.Dialog.HotNormal = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.Gray);
+			Colors.Dialog.HotFocus = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.DarkGray);
+
+			Colors.Error.Normal = MakeColor (ConsoleColor.DarkRed, ConsoleColor.White);
+			Colors.Error.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkRed);
+			Colors.Error.HotNormal = MakeColor (ConsoleColor.Black, ConsoleColor.White);
+			Colors.Error.HotFocus = MakeColor (ConsoleColor.Black, ConsoleColor.DarkRed);
 		}
 		}
 
 
 		void ResizeScreen ()
 		void ResizeScreen ()
@@ -1196,12 +1181,15 @@ namespace Terminal.Gui {
 
 
 		void UpdateOffScreen ()
 		void UpdateOffScreen ()
 		{
 		{
-			for (int row = 0; row < rows; row++)
+			for (int row = 0; row < rows; row++) {
 				for (int col = 0; col < cols; col++) {
 				for (int col = 0; col < cols; col++) {
 					int position = row * cols + col;
 					int position = row * cols + col;
 					OutputBuffer [position].Attributes = (ushort)Colors.TopLevel.Normal;
 					OutputBuffer [position].Attributes = (ushort)Colors.TopLevel.Normal;
 					OutputBuffer [position].Char.UnicodeChar = ' ';
 					OutputBuffer [position].Char.UnicodeChar = ' ';
 				}
 				}
+			}
+
+			winChanging = false;
 		}
 		}
 
 
 		int ccol, crow;
 		int ccol, crow;
@@ -1245,7 +1233,6 @@ namespace Terminal.Gui {
 		}
 		}
 
 
 		int currentAttribute;
 		int currentAttribute;
-		CancellationTokenSource tokenSource = new CancellationTokenSource ();
 
 
 		public override void SetAttribute (Attribute c)
 		public override void SetAttribute (Attribute c)
 		{
 		{
@@ -1298,16 +1285,16 @@ namespace Terminal.Gui {
 				Y = (short)Clip.Height
 				Y = (short)Clip.Height
 			};
 			};
 
 
-			var window = new WindowsConsole.SmallRect () {
-				Top = 0,
-				Left = 0,
-				Right = (short)Clip.Right,
-				Bottom = (short)Clip.Bottom
-			};
+			//var window = new WindowsConsole.SmallRect () {
+			//	Top = 0,
+			//	Left = 0,
+			//	Right = (short)Clip.Right,
+			//	Bottom = (short)Clip.Bottom
+			//};
 
 
 			UpdateCursor ();
 			UpdateCursor ();
 			winConsole.WriteToConsole (OutputBuffer, bufferCoords, damageRegion);
 			winConsole.WriteToConsole (OutputBuffer, bufferCoords, damageRegion);
-			//			System.Diagnostics.Debugger.Log(0, "debug", $"Region={damageRegion.Right - damageRegion.Left},{damageRegion.Bottom - damageRegion.Top}\n");
+			//System.Diagnostics.Debugger.Log(0, "debug", $"Region={damageRegion.Right - damageRegion.Left},{damageRegion.Bottom - damageRegion.Top}\n");
 			WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
 			WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
 		}
 		}
 
 
@@ -1325,7 +1312,7 @@ namespace Terminal.Gui {
 			winConsole.Cleanup ();
 			winConsole.Cleanup ();
 		}
 		}
 
 
-#region Unused
+		#region Unused
 		public override void SetColors (ConsoleColor foreground, ConsoleColor background)
 		public override void SetColors (ConsoleColor foreground, ConsoleColor background)
 		{
 		{
 		}
 		}
@@ -1353,8 +1340,172 @@ namespace Terminal.Gui {
 		public override void CookMouse ()
 		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;
+		WindowsConsole.WindowPlacement windowPlacement;
+		Size windowSize;
+		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;
+
+		/// <summary>
+		/// Invoked when the window is changed.
+		/// </summary>
+		public Action<Size> WinChanged;
+
+		public WindowsMainLoop (ConsoleDriver consoleDriver = null)
+		{
+			if (consoleDriver == null) {
+				throw new ArgumentNullException ("Console driver instance must be provided.");
+			}
+			this.consoleDriver = consoleDriver;
+			winConsole = ((WindowsDriver)consoleDriver).WinConsole;
+		}
+
+		void IMainLoopDriver.Setup (MainLoop mainLoop)
+		{
+			this.mainLoop = mainLoop;
+			Task.Run (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) {
+				if (!consoleDriver.HeightAsBuffer) {
+					windowPlacement = winConsole.GetWindow ();
+					if (windowPlacement.rcNormalPosition.Size.Height > -1) {
+						windowSize = new Size (Math.Max (((windowPlacement.rcNormalPosition.Size.Width -
+							windowPlacement.rcNormalPosition.X) / 8) - 2, 0),
+							Math.Max (((windowPlacement.rcNormalPosition.Size.Height -
+							windowPlacement.rcNormalPosition.Y) / 16) - 7, 0));
+						if (windowPlacement.showCmd != WindowsConsole.SHOW_MAXIMIZED
+							&& (windowSize.Width != consoleDriver.Cols || windowSize.Height != consoleDriver.Rows)) {
+							return;
+						} else if (windowPlacement.showCmd == WindowsConsole.SHOW_MAXIMIZED
+							&& (Console.LargestWindowWidth != consoleDriver.Cols || Console.LargestWindowHeight != consoleDriver.Rows)) {
+							windowSize = new Size (Console.LargestWindowWidth, Console.LargestWindowHeight);
+							return;
+						}
+					}
+				}
+			}
+		}
+
+		void IMainLoopDriver.Wakeup ()
+		{
+			//tokenSource.Cancel ();
+			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) || winChanged;
+			}
+
+			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) {
+				var inputEvent = result [0];
+				result = null;
+				ProcessInput?.Invoke (inputEvent);
+			}
+			if (winChanged) {
+				winChanged = false;
+				WinChanged?.Invoke (windowSize);
+			}
+		}
+	}
 }
 }

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

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

+ 9 - 5
Terminal.Gui/Core/ConsoleDriver.cs

@@ -546,6 +546,12 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// </summary>
 		public abstract int Top { get; }
 		public abstract int Top { get; }
 
 
+		/// <summary>
+		/// If false height is measured by the window height and thus no scrolling.
+		/// If true then height is measured by the buffer height, enabling scrolling.
+		/// </summary>
+		public abstract bool HeightAsBuffer { get; set; }
+
 		/// <summary>
 		/// <summary>
 		/// Initializes the driver
 		/// Initializes the driver
 		/// </summary>
 		/// </summary>
@@ -570,12 +576,10 @@ namespace Terminal.Gui {
 		/// <returns></returns>
 		/// <returns></returns>
 		public static Rune MakePrintable (Rune c)
 		public static Rune MakePrintable (Rune c)
 		{
 		{
-			if (c <= 0x1F) {
-				// ASCII (C0) control characters. 
-				return new Rune (c + 0x2400);
-			} else if (c >= 0x80 && c <= 0x9F) {
+			if (c <= 0x1F || (c >= 0x80 && c <= 0x9F)) {
+				// ASCII (C0) control characters.
 				// C1 control characters (https://www.aivosto.com/articles/control-characters.html#c1)
 				// C1 control characters (https://www.aivosto.com/articles/control-characters.html#c1)
-				return new Rune (0x25a1); // U+25A1, WHITE SQUARE, □: 
+				return new Rune (c + 0x2400);
 			} else {
 			} else {
 				return c;
 				return c;
 			}
 			}

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

@@ -401,10 +401,10 @@ namespace Terminal.Gui {
 		{
 		{
 			EnsureVisibleBounds (top, top.Frame.X, top.Frame.Y, out int nx, out int ny);
 			EnsureVisibleBounds (top, top.Frame.X, top.Frame.Y, out int nx, out int ny);
 			if ((nx != top.Frame.X || ny != top.Frame.Y) && top.LayoutStyle == LayoutStyle.Computed) {
 			if ((nx != top.Frame.X || ny != top.Frame.Y) && top.LayoutStyle == LayoutStyle.Computed) {
-				if (top.X is Pos.PosAbsolute && top.Bounds.X != nx) {
+				if ((top.X == null || top.X is Pos.PosAbsolute) && top.Bounds.X != nx) {
 					top.X = nx;
 					top.X = nx;
 				}
 				}
-				if (top.Y is Pos.PosAbsolute && top.Bounds.Y != ny) {
+				if ((top.Y == null || top.Y is Pos.PosAbsolute) && top.Bounds.Y != ny) {
 					top.Y = ny;
 					top.Y = ny;
 				}
 				}
 			}
 			}

+ 7 - 3
Terminal.Gui/Core/View.cs

@@ -1314,10 +1314,13 @@ namespace Terminal.Gui {
 				return;
 				return;
 			}
 			}
 
 
+			Application.CurrentView = this;
+
 			var clipRect = new Rect (Point.Empty, frame.Size);
 			var clipRect = new Rect (Point.Empty, frame.Size);
 
 
-			if (ColorScheme != null)
+			if (ColorScheme != null) {
 				Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal);
 				Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal);
+			}
 
 
 			if (!ustring.IsNullOrEmpty (Text)) {
 			if (!ustring.IsNullOrEmpty (Text)) {
 				Clear ();
 				Clear ();
@@ -1333,14 +1336,14 @@ namespace Terminal.Gui {
 
 
 			if (subviews != null) {
 			if (subviews != null) {
 				foreach (var view in subviews) {
 				foreach (var view in subviews) {
-					if (!view.NeedDisplay.IsEmpty || view.ChildNeedsDisplay) {
+					if (!view.NeedDisplay.IsEmpty || view.ChildNeedsDisplay || view.LayoutNeeded) {
 						if (view.Frame.IntersectsWith (clipRect) && (view.Frame.IntersectsWith (bounds) || bounds.X < 0 || bounds.Y < 0)) {
 						if (view.Frame.IntersectsWith (clipRect) && (view.Frame.IntersectsWith (bounds) || bounds.X < 0 || bounds.Y < 0)) {
 							if (view.LayoutNeeded)
 							if (view.LayoutNeeded)
 								view.LayoutSubviews ();
 								view.LayoutSubviews ();
 							Application.CurrentView = view;
 							Application.CurrentView = view;
 
 
 							// Draw the subview
 							// Draw the subview
-							// Use the view's bounds (view-relative; Location will always be (0,0) because
+							// Use the view's bounds (view-relative; Location will always be (0,0)
 							if (view.Visible) {
 							if (view.Visible) {
 								view.Redraw (view.Bounds);
 								view.Redraw (view.Bounds);
 							}
 							}
@@ -1350,6 +1353,7 @@ namespace Terminal.Gui {
 					}
 					}
 				}
 				}
 			}
 			}
+			ClearLayoutNeeded ();
 			ClearNeedsDisplay ();
 			ClearNeedsDisplay ();
 		}
 		}
 
 

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

@@ -1415,7 +1415,7 @@ namespace Terminal.Gui {
 				CloseMenu ();
 				CloseMenu ();
 				if (openedByAltKey) {
 				if (openedByAltKey) {
 					openedByAltKey = false;
 					openedByAltKey = false;
-					LastFocused.SetFocus ();
+					LastFocused?.SetFocus ();
 				}
 				}
 				break;
 				break;
 
 

+ 27 - 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.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Text;
 using System.Text;
@@ -58,15 +61,20 @@ namespace UICatalog {
 				CreateRadio("End", CharMap.MaxCodePointVal - 16, CharMap.MaxCodePointVal),
 				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) => {
 			jumpList.SelectedItemChanged += (args) => {
-				_charMap.Start = radioItems[args.SelectedItem].start;
+				_charMap.Start = radioItems [args.SelectedItem].start;
 			};
 			};
 
 
 			Win.Add (jumpList);
 			Win.Add (jumpList);
+
+			jumpList.Refresh ();
+			jumpList.SetFocus ();
 		}
 		}
 
 
 		public override void Run ()
 		public override void Run ()
@@ -91,11 +99,14 @@ namespace UICatalog {
 		}
 		}
 		int _start = 0x2500;
 		int _start = 0x2500;
 
 
+		public const int H_SPACE = 2;
+		public const int V_SPACE = 2;
+
 		public static int MaxCodePointVal => 0xE0FFF;
 		public static int MaxCodePointVal => 0xE0FFF;
 
 
 		// Row Header + space + (space + char + space)
 		// Row Header + space + (space + char + space)
 		public static int RowHeaderWidth => $"U+{MaxCodePointVal:x5}".Length;
 		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 ()
 		public CharMap ()
 		{
 		{
@@ -109,10 +120,12 @@ namespace UICatalog {
 					ShowHorizontalScrollIndicator = false;
 					ShowHorizontalScrollIndicator = false;
 				}
 				}
 			};
 			};
+#if DRAW_CONTENT
 
 
 			DrawContent += CharMap_DrawContent;
 			DrawContent += CharMap_DrawContent;
+#endif
 		}
 		}
-#if true
+
 		private void CharMap_DrawContent (Rect viewport)
 		private void CharMap_DrawContent (Rect viewport)
 		{
 		{
 			//Rune ReplaceNonPrintables (Rune c)
 			//Rune ReplaceNonPrintables (Rune c)
@@ -125,10 +138,10 @@ namespace UICatalog {
 			//}
 			//}
 
 
 			for (int header = 0; header < 16; header++) {
 			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} ");
 				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;
 				int val = (-viewport.Y + row) * 16;
 				if (val < MaxCodePointVal) {
 				if (val < MaxCodePointVal) {
 					var rowLabel = $"U+{val / 16:x4}x";
 					var rowLabel = $"U+{val / 16:x4}x";
@@ -137,17 +150,17 @@ namespace UICatalog {
 					var prevColWasWide = false;
 					var prevColWasWide = false;
 					for (int col = 0; col < 16; col++) {
 					for (int col = 0; col < 16; col++) {
 						var rune = new Rune ((uint)((uint)(-viewport.Y + row) * 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);
 						Driver.AddRune (rune);
-						//prevColWasWide = Rune.ColumnWidth(rune) > 1;
+						//prevColWasWide = Rune.ColumnWidth (rune) > 1;
 					}
 					}
 				}
 				}
 			}
 			}
 		}
 		}
-#else
+#if BASE_DRAW_CONTENT
 		public override void OnDrawContent (Rect viewport)
 		public override void OnDrawContent (Rect viewport)
 		{
 		{
-			CharMap_DrawContent(this, viewport);
+			CharMap_DrawContent (viewport);
 			base.OnDrawContent (viewport);
 			base.OnDrawContent (viewport);
 		}
 		}
 #endif
 #endif