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

This commit is contained in:
BDisp
2020-12-02 17:46:20 +00:00
parent 62b5800c84
commit eec477a89f
2 changed files with 203 additions and 66 deletions

View File

@@ -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 placement;
return GetWindowPlacement (handle, ref placement);
}
internal void SetWindow (int showCmd)
internal bool SetWindow (IntPtr handle, ref WindowPlacement placement)
{
IntPtr thisConsole = GetConsoleWindow ();
WindowPlacement placement = new WindowPlacement {
length = Marshal.SizeOf (typeof (WindowPlacement)),
showCmd = showCmd
};
SetWindowPlacement (thisConsole, ref placement);
return SetWindowPlacement (handle, ref placement);
}
[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)
{
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,27 +1461,93 @@ 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;
}
}
void IMainLoopDriver.Wakeup ()
@@ -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 ();

View File

@@ -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";