浏览代码

Fixes #1028. WindowsDriver does not handle Window docking properly with the -usc argument.

BDisp 4 年之前
父节点
当前提交
eec477a89f
共有 2 个文件被更改,包括 201 次插入64 次删除
  1. 179 64
      Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
  2. 22 0
      UICatalog/UICatalog.cs

+ 179 - 64
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -59,34 +59,13 @@ namespace Terminal.Gui {
 		public bool WriteToConsole (CharInfo [] charInfoBuffer, Coord coords, SmallRect window)
 		{
 			if (ScreenBuffer == IntPtr.Zero) {
-				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 [Console.WindowHeight * Console.WindowWidth];
-
-				ReadConsoleOutput (OutputHandle, OriginalStdOutChars, coords, new Coord () { X = 0, Y = 0 }, ref window);
+				window = ReadFromConsoleOutput (new Size (Console.WindowWidth, Console.WindowHeight), coords, window);
 			}
 
 			return WriteConsoleOutput (ScreenBuffer, charInfoBuffer, coords, new Coord () { X = window.Left, Y = window.Top }, ref window);
 		}
 
-		public void ReadFromConsoleOutput (Size size, Coord coords)
+		public SmallRect ReadFromConsoleOutput (Size size, Coord coords, SmallRect window)
 		{
 			ScreenBuffer = CreateConsoleScreenBuffer (
 				DesiredAccess.GenericRead | DesiredAccess.GenericWrite,
@@ -98,18 +77,22 @@ namespace Terminal.Gui {
 			if (ScreenBuffer == INVALID_HANDLE_VALUE) {
 				var err = Marshal.GetLastWin32Error ();
 
-				if (err != 0)
+				if (err != 0 && HeightAsBuffer) {
 					throw new System.ComponentModel.Win32Exception (err);
+				}
 			}
 
 			if (!SetConsoleActiveScreenBuffer (ScreenBuffer)) {
 				var err = Marshal.GetLastWin32Error ();
-				throw new System.ComponentModel.Win32Exception (err);
+				if (HeightAsBuffer) {
+					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);
+
+			return window;
 		}
 
 		public bool SetCursorPosition (Coord position)
@@ -146,6 +129,8 @@ namespace Terminal.Gui {
 			}
 		}
 
+		public bool HeightAsBuffer { get; set; }
+
 		[Flags]
 		public enum ConsoleModes : uint {
 			EnableProcessedInput = 1,
@@ -483,6 +468,11 @@ namespace Terminal.Gui {
 		[DllImport ("kernel32.dll", ExactSpelling = true)]
 		static extern IntPtr GetConsoleWindow ();
 
+		internal IntPtr GetConsole ()
+		{
+			return GetConsoleWindow ();
+		}
+
 		[DllImport ("user32.dll")]
 		[return: MarshalAs (UnmanagedType.Bool)]
 		static extern bool GetWindowPlacement (IntPtr hWnd, ref WindowPlacement lpwndpl);
@@ -498,7 +488,9 @@ namespace Terminal.Gui {
 			public System.Drawing.Point ptMinPosition;
 			public System.Drawing.Point ptMaxPosition;
 			public System.Drawing.Rectangle rcNormalPosition;
+#if _MAC
 			public System.Drawing.Rectangle rcDevice;
+#endif
 		}
 
 		// flags
@@ -508,7 +500,7 @@ namespace Terminal.Gui {
 
 		// showCmd
 		public const int HIDE = 0;
-		public const int NORMAL = 1;
+		public const int SHOW_NORMAL = 1;
 		public const int SHOW_MINIMIZED = 2;
 		public const int SHOW_MAXIMIZED = 3;
 		public const int SHOW_NOACTIVATE = 4;
@@ -520,28 +512,62 @@ namespace Terminal.Gui {
 		public const int SHOW_DEFAULT = 10;
 		public const int FORCE_MINIMIZE = 11;
 
-		internal WindowPlacement GetWindow ()
+		internal bool GetWindow (IntPtr handle, ref WindowPlacement placement)
 		{
-			IntPtr thisConsole = GetConsoleWindow ();
-			WindowPlacement placement = new WindowPlacement {
+			placement = new WindowPlacement {
 				length = Marshal.SizeOf (typeof (WindowPlacement))
 			};
-			GetWindowPlacement (thisConsole, ref placement);
+			return GetWindowPlacement (handle, ref placement);
+		}
 
-			return placement;
+		internal bool SetWindow (IntPtr handle, ref WindowPlacement placement)
+		{
+			return SetWindowPlacement (handle, ref placement);
 		}
 
-		internal void SetWindow (int showCmd)
+		[DllImport ("user32.dll", SetLastError = true)]
+		static extern bool GetWindowRect (IntPtr hwnd, out System.Drawing.Rectangle lpRect);
+
+		internal bool GetRect (IntPtr handle, out System.Drawing.Rectangle lpRect)
 		{
-			IntPtr thisConsole = GetConsoleWindow ();
-			WindowPlacement placement = new WindowPlacement {
-				length = Marshal.SizeOf (typeof (WindowPlacement)),
-				showCmd = showCmd
-			};
-			SetWindowPlacement (thisConsole, ref placement);
+			return GetWindowRect (handle, out lpRect);
 		}
 
 #if false
+		// size of a device name string
+		private const int CCHDEVICENAME = 32;
+
+		[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Auto)]
+		internal struct MonitorInfoEx {
+			public uint cbSize;
+			public System.Drawing.Rectangle rcMonitor;
+			public System.Drawing.Rectangle rcWork;
+			public int dwFlags;
+			[MarshalAs (UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
+			public string szDevice;
+		}
+
+		[DllImport ("user32.dll", CharSet = CharSet.Auto)]
+		static extern bool GetMonitorInfo (IntPtr hMonitor, ref MonitorInfoEx lpmi);
+
+		internal bool GetMonitor(IntPtr hMonitor, ref MonitorInfoEx minfo)
+		{
+			minfo.cbSize = (uint)Marshal.SizeOf (minfo);
+			return GetMonitorInfo (hMonitor, ref minfo);
+		}
+
+		[DllImport ("user32.dll")]
+		static extern IntPtr MonitorFromWindow (IntPtr hwnd, uint dwFlags);
+
+		public const int MONITOR_DEFAULTTONULL = 0;
+		public const int MONITOR_DEFAULTTOPRIMARY = 1;
+		public const int MONITOR_DEFAULTTONEAREST = 2;
+
+		internal IntPtr GetMonitorWindow (IntPtr hwnd, uint dwFlag)
+		{
+			return MonitorFromWindow (hwnd, dwFlag);
+		}
+
 		[DllImport ("kernel32.dll", SetLastError = true)]
 		static extern bool GetConsoleScreenBufferInfo (IntPtr hConsoleOutput, out ConsoleScreenBufferInfo ConsoleScreenBufferInfo);
 
@@ -580,10 +606,12 @@ namespace Terminal.Gui {
 
 		public WindowsDriver ()
 		{
-			winConsole = new WindowsConsole ();
+			winConsole = new WindowsConsole () {
+				HeightAsBuffer = this.HeightAsBuffer
+			};
 		}
 
-		bool winChanging;
+		MainLoop mainLoop;
 
 		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
 		{
@@ -591,6 +619,7 @@ namespace Terminal.Gui {
 			this.keyDownHandler = keyDownHandler;
 			this.keyUpHandler = keyUpHandler;
 			this.mouseHandler = mouseHandler;
+			this.mainLoop = mainLoop;
 
 			var mLoop = mainLoop.Driver as WindowsMainLoop;
 
@@ -599,6 +628,9 @@ namespace Terminal.Gui {
 			mLoop.WinChanged = (e) => ChangeWin (e);
 		}
 
+		bool winChanging;
+		bool wasChangeWin;
+
 		void ChangeWin (Size size)
 		{
 			if (!HeightAsBuffer) {
@@ -606,21 +638,22 @@ namespace Terminal.Gui {
 				top = 0;
 				cols = size.Width;
 				rows = size.Height;
+				ResizeScreen ();
+				UpdateOffScreen ();
 				var bufferCoords = new WindowsConsole.Coord () {
 					X = (short)cols,
 					Y = (short)rows
 				};
-				winConsole.ReadFromConsoleOutput (size, bufferCoords);
-				ResizeScreen ();
-				UpdateOffScreen ();
+				winConsole.ReadFromConsoleOutput (size, bufferCoords, damageRegion);
 				if (!winChanging) {
 					TerminalResized.Invoke ();
-				} else {
-					System.Diagnostics.Debugger.Break ();
 				}
+				wasChangeWin = true;
 			}
 		}
 
+		bool isFromRestore;
+
 		void ProcessInput (WindowsConsole.InputRecord inputEvent)
 		{
 			switch (inputEvent.EventType) {
@@ -718,6 +751,18 @@ namespace Terminal.Gui {
 					ResizeScreen ();
 					UpdateOffScreen ();
 					TerminalResized?.Invoke ();
+				} else if (!HeightAsBuffer && !wasChangeWin && !(mainLoop.Driver as WindowsMainLoop).Maximized
+					&& !isFromRestore) {
+					ChangeWin (new Size (inputEvent.WindowBufferSizeEvent.size.X,
+						inputEvent.WindowBufferSizeEvent.size.Y));
+				} else if (!HeightAsBuffer && wasChangeWin && (mainLoop.Driver as WindowsMainLoop).Restored) {
+					(mainLoop.Driver as WindowsMainLoop).Restored = false;
+					isFromRestore = true;
+				} else if (!HeightAsBuffer && wasChangeWin && !(mainLoop.Driver as WindowsMainLoop).Maximized
+					&& !(mainLoop.Driver as WindowsMainLoop).Restored && !isFromRestore) {
+					wasChangeWin = false;
+				} else if (isFromRestore) {
+					isFromRestore = false;
 				}
 				break;
 
@@ -1358,7 +1403,6 @@ namespace Terminal.Gui {
 		ConsoleDriver consoleDriver;
 		WindowsConsole winConsole;
 		bool winChanged;
-		WindowsConsole.WindowPlacement windowPlacement;
 		Size windowSize;
 		CancellationTokenSource tokenSource = new CancellationTokenSource ();
 
@@ -1375,6 +1419,9 @@ namespace Terminal.Gui {
 		/// </summary>
 		public Action<Size> WinChanged;
 
+		public bool Maximized;
+		public bool Restored;
+
 		public WindowsMainLoop (ConsoleDriver consoleDriver = null)
 		{
 			if (consoleDriver == null) {
@@ -1414,26 +1461,92 @@ namespace Terminal.Gui {
 			}
 		}
 
+		const int Width_Divider = 8;
+		const int Height_Divider = 18;
+		bool docked;
+
 		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;
+			var handle = winConsole.GetConsole ();
+
+			while (!consoleDriver.HeightAsBuffer) {
+				WindowsConsole.WindowPlacement windowPlacement = new WindowsConsole.WindowPlacement ();
+				winConsole.GetWindow (handle, ref windowPlacement);
+
+				if (windowPlacement.rcNormalPosition.Size.Height > -1) {
+					windowSize = SetWindowSize (windowPlacement.rcNormalPosition);
+
+					if (windowPlacement.showCmd != WindowsConsole.SHOW_MAXIMIZED && !Maximized && !Restored && !docked
+						&& (windowSize.Width != consoleDriver.Cols || windowSize.Height != consoleDriver.Rows)) {
+						docked = false;
+						return;
+					} else if (windowPlacement.showCmd == WindowsConsole.SHOW_MAXIMIZED && !Maximized
+						&& (Console.LargestWindowWidth != consoleDriver.Cols || Console.LargestWindowHeight != consoleDriver.Rows)) {
+						windowSize = new Size (Console.LargestWindowWidth, Console.LargestWindowHeight);
+						Maximized = true;
+						docked = false;
+						return;
+					} else if (windowPlacement.showCmd != WindowsConsole.SHOW_MAXIMIZED && Maximized) {
+						windowPlacement = new WindowsConsole.WindowPlacement () {
+							showCmd = WindowsConsole.RESTORE
+						};
+						winConsole.SetWindow (handle, ref windowPlacement);
+						Restored = true;
+						Maximized = false;
+						docked = false;
+						return;
+					} else if (!Maximized && IsDockedToMonitor (handle, windowPlacement)) {
+						return;
+					}
+				}
+			}
+
+			Size SetWindowSize (System.Drawing.Rectangle rect)
+			{
+				return new Size (Math.Max (((rect.Width - rect.X) / Width_Divider) - 2, 0),
+					Math.Max (((rect.Height - rect.Y) / Height_Divider) - 2, 0));
+			}
+
+			bool IsDockedToMonitor (IntPtr hWnd, WindowsConsole.WindowPlacement placement)
+			{
+				System.Drawing.Rectangle rc;
+				winConsole.GetRect (hWnd, out rc);
+
+				var changed = placement.showCmd == WindowsConsole.SHOW_NORMAL
+				    && (rc.Left != placement.rcNormalPosition.Left ||
+					rc.Top != placement.rcNormalPosition.Top ||
+					rc.Right != placement.rcNormalPosition.Right ||
+					rc.Bottom != placement.rcNormalPosition.Bottom);
+
+				if (changed) {
+					var pSize = new Size (placement.rcNormalPosition.Size.Width - placement.rcNormalPosition.X,
+						placement.rcNormalPosition.Size.Height - placement.rcNormalPosition.Y);
+					var rSize = new Size (rc.Width - rc.X,
+						rc.Height - rc.Y);
+					windowSize = SetWindowSize (rc);
+
+					if ((rc.X < 0) || (rc.Y == 0) || (rc.Y == 0 && rc.X < 0)
+						|| (rc.Y == 0 && rc.Right / Width_Divider >= Console.LargestWindowWidth)
+						|| (rc.X < 0 && rc.Bottom / Height_Divider >= Console.LargestWindowHeight)
+						|| (rc.X / Width_Divider >= Console.LargestWindowWidth / 2 - 1 && rc.Bottom / Height_Divider >= Console.LargestWindowHeight)) {
+						if (!docked || consoleDriver.Cols != windowSize.Width
+							|| consoleDriver.Rows != windowSize.Height) {
+							docked = true;
+						} else {
+							changed = false;
+						}
+					} else {
+						if (!docked && (pSize == rSize || rSize.Width / Width_Divider >= Console.LargestWindowWidth
+							|| rSize.Height / Height_Divider >= Console.LargestWindowHeight)) {
+							changed = false;
 						}
+						docked = false;
 					}
+				} else {
+					docked = false;
 				}
+
+				return changed;
 			}
 		}
 
@@ -1451,7 +1564,9 @@ namespace Terminal.Gui {
 
 			//result = null;
 			waitForProbe.Set ();
-			winChange.Set ();
+			if (!consoleDriver.HeightAsBuffer) {
+				winChange.Set ();
+			}
 
 			try {
 				if (!tokenSource.IsCancellationRequested) {
@@ -1464,7 +1579,7 @@ namespace Terminal.Gui {
 			}
 
 			if (!tokenSource.IsCancellationRequested) {
-				return result != null || CheckTimers (wait, out waitTimeout) || winChanged;
+				return result != null || CheckTimers (wait, out _) || winChanged;
 			}
 
 			tokenSource.Dispose ();

+ 22 - 0
UICatalog/UICatalog.cs

@@ -167,6 +167,7 @@ namespace UICatalog {
 				}),
 				new MenuBarItem ("_Color Scheme", CreateColorSchemeMenuItems()),
 				new MenuBarItem ("Diag_nostics", CreateDiagnosticMenuItems()),
+				new MenuBarItem ("_Size Style", CreateSizeStyle()),
 				new MenuBarItem ("_Help", new MenuItem [] {
 					new MenuItem ("_gui.cs API Overview", "", () => OpenUrl ("https://migueldeicaza.github.io/gui.cs/articles/overview.html"), null, null, Key.F1),
 					new MenuItem ("gui.cs _README", "", () => OpenUrl ("https://github.com/migueldeicaza/gui.cs"), null, null, Key.F2),
@@ -274,6 +275,27 @@ namespace UICatalog {
 			return _runningScenario;
 		}
 
+		static MenuItem [] CreateSizeStyle ()
+		{
+			List<MenuItem> menuItems = new List<MenuItem> ();
+			for (int i = 0; i < 2; i++) {
+				var item = new MenuItem ();
+				item.Title = i == 0 ? "_Normal" : "_As Buffer";
+				item.Shortcut = Key.CtrlMask | Key.AltMask | (Key)item.Title.ToString ().Substring (1, 1) [0];
+				item.CheckType |= MenuItemCheckStyle.Radio;
+				item.Checked = i == 0 && !Application.HeightAsBuffer ? true : false;
+				item.Action += () => {
+					item.Checked = !item.Checked;
+					Application.HeightAsBuffer = item.Title == "_Normal" ? false : true;
+					foreach (var menuItem in menuItems) {
+						menuItem.Checked = menuItem.Title.Equals (item.Title);
+					}
+				};
+				menuItems.Add (item);
+			}
+			return menuItems.ToArray ();
+		}
+
 		static MenuItem [] CreateDiagnosticMenuItems ()
 		{
 			const string OFF = "Diagnostics: _Off";