diff --git a/Terminal.Gui/Drivers/NetDriver/NetDriver.cs b/Terminal.Gui/Drivers/NetDriver/NetDriver.cs index fa2ccadd9..aec306da5 100644 --- a/Terminal.Gui/Drivers/NetDriver/NetDriver.cs +++ b/Terminal.Gui/Drivers/NetDriver/NetDriver.cs @@ -339,7 +339,37 @@ internal class NetDriver : ConsoleDriver Left = 0; Cols = inputEvent.WindowSizeEvent.Size.Width; Rows = Math.Max (inputEvent.WindowSizeEvent.Size.Height, 0); - ; + + if (!RunningUnitTests) + { + if (IsWinPlatform) + { + try + { + Size newSize = NetWinConsole!.SetConsoleWindow ((short)Cols, (short)Rows); + + if (Cols != newSize.Width) + { + Cols = newSize.Width; + } + + if (Rows != newSize.Height) + { + Rows = newSize.Height; + } + } + catch (Exception e) + { + // If we can't resize the console, we just log the error. + Console.WriteLine (e); + } + } + else + { + Console.Out.Write (EscSeqUtils.CSI_SetTerminalWindowSize (Rows, Cols)); + } + } + ResizeScreen (); ClearContents (); _winSizeChanging = false; @@ -744,49 +774,8 @@ internal class NetDriver : ConsoleDriver } } - public virtual void ResizeScreen () + public void ResizeScreen () { - // Not supported on Unix. - if (IsWinPlatform) - { - // Can raise an exception while is still resizing. - try - { -#pragma warning disable CA1416 - if (Console.WindowHeight > 0) - { - Console.CursorTop = 0; - Console.CursorLeft = 0; - Console.WindowTop = 0; - Console.WindowLeft = 0; - - if (Console.WindowHeight > Rows) - { - Console.SetWindowSize (Cols, Rows); - } - - Console.SetBufferSize (Cols, Rows); - } -#pragma warning restore CA1416 - } - // INTENT: Why are these eating the exceptions? - // Comments would be good here. - catch (IOException) - { - // CONCURRENCY: Unsynchronized access to Clip is not safe. - Clip = new (Screen); - } - catch (ArgumentOutOfRangeException) - { - // CONCURRENCY: Unsynchronized access to Clip is not safe. - Clip = new (Screen); - } - } - else - { - Console.Out.Write (EscSeqUtils.CSI_SetTerminalWindowSize (Rows, Cols)); - } - // CONCURRENCY: Unsynchronized access to Clip is not safe. Clip = new (Screen); } diff --git a/Terminal.Gui/Drivers/NetDriver/NetEvents.cs b/Terminal.Gui/Drivers/NetDriver/NetEvents.cs index 94b01baaf..fb8f22bd0 100644 --- a/Terminal.Gui/Drivers/NetDriver/NetEvents.cs +++ b/Terminal.Gui/Drivers/NetDriver/NetEvents.cs @@ -143,6 +143,8 @@ internal class NetEvents : IDisposable ); } + private Size? _lastWindowSizeBeforeMaximized = null; + private void CheckWindowSizeChange () { void RequestWindowSize () @@ -151,25 +153,50 @@ internal class NetEvents : IDisposable { // Wait for a while then check if screen has changed sizes Task.Delay (500, _netEventsDisposed.Token).Wait (_netEventsDisposed.Token); - - int buffHeight, buffWidth; + Size windowSize; if (((NetDriver)_consoleDriver).IsWinPlatform) { - buffHeight = Math.Max (Console.BufferHeight, 0); - buffWidth = Math.Max (Console.BufferWidth, 0); + Size largestWindowSize = ((NetDriver)_consoleDriver).NetWinConsole!.GetLargestConsoleWindowSize (); + windowSize = ((NetDriver)_consoleDriver).NetWinConsole!.GetConsoleBufferWindow (out _); + + if (_lastWindowSizeBeforeMaximized is null && windowSize == largestWindowSize) + { + _lastWindowSizeBeforeMaximized = new (_consoleDriver.Cols, _consoleDriver.Rows); + } + else if (_lastWindowSizeBeforeMaximized is { } && windowSize != largestWindowSize) + { + if (windowSize != _lastWindowSizeBeforeMaximized) + { + windowSize = new (_lastWindowSizeBeforeMaximized.Value.Width, _lastWindowSizeBeforeMaximized.Value.Height); + + while (Console.WindowWidth != _lastWindowSizeBeforeMaximized.Value.Width + && Console.WindowHeight != _lastWindowSizeBeforeMaximized.Value.Height) + { + try + { + windowSize = ((NetDriver)_consoleDriver).NetWinConsole!.SetConsoleWindow ( + (short)_lastWindowSizeBeforeMaximized.Value.Width, + (short)_lastWindowSizeBeforeMaximized.Value.Height); + } + catch (Exception e) + { + Console.WriteLine (e); + } + } + } + + _lastWindowSizeBeforeMaximized = null; + } } else { - buffHeight = _consoleDriver.Rows; - buffWidth = _consoleDriver.Cols; + windowSize = new (Console.WindowWidth, Console.WindowHeight); } if (EnqueueWindowSizeEvent ( - Math.Max (Console.WindowHeight, 0), - Math.Max (Console.WindowWidth, 0), - buffHeight, - buffWidth + Math.Max (windowSize.Height, 0), + Math.Max (windowSize.Width, 0) )) { return; @@ -198,10 +225,8 @@ internal class NetEvents : IDisposable /// Enqueue a window size event if the window size has changed. /// /// - /// - /// /// - private bool EnqueueWindowSizeEvent (int winHeight, int winWidth, int buffHeight, int buffWidth) + private bool EnqueueWindowSizeEvent (int winHeight, int winWidth) { if (winWidth == _consoleDriver.Cols && winHeight == _consoleDriver.Rows) { @@ -446,8 +471,6 @@ internal class NetEvents : IDisposable { case EscSeqUtils.CSI_ReportTerminalSizeInChars_ResponseValue: EnqueueWindowSizeEvent ( - Math.Max (int.Parse (values [1]), 0), - Math.Max (int.Parse (values [2]), 0), Math.Max (int.Parse (values [1]), 0), Math.Max (int.Parse (values [2]), 0) ); diff --git a/Terminal.Gui/Drivers/NetDriver/NetWinVTConsole.cs b/Terminal.Gui/Drivers/NetDriver/NetWinVTConsole.cs index 4750a32e8..247f1cf58 100644 --- a/Terminal.Gui/Drivers/NetDriver/NetWinVTConsole.cs +++ b/Terminal.Gui/Drivers/NetDriver/NetWinVTConsole.cs @@ -1,4 +1,5 @@ #nullable enable +using System.ComponentModel; using System.Runtime.InteropServices; namespace Terminal.Gui.Drivers; @@ -112,6 +113,85 @@ internal class NetWinVTConsole } } + internal Size GetConsoleBufferWindow (out Point position) + { + if (_outputHandle == nint.Zero) + { + position = Point.Empty; + + return Size.Empty; + } + + var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); + csbi.cbSize = (uint)Marshal.SizeOf (csbi); + + if (!GetConsoleScreenBufferInfoEx (_outputHandle, ref csbi)) + { + //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + position = Point.Empty; + + return Size.Empty; + } + + Size sz = new ( + csbi.srWindow.Right - csbi.srWindow.Left + 1, + csbi.srWindow.Bottom - csbi.srWindow.Top + 1); + position = new (csbi.srWindow.Left, csbi.srWindow.Top); + + return sz; + } + + internal Size SetConsoleWindow (short cols, short rows) + { + var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX (); + csbi.cbSize = (uint)Marshal.SizeOf (csbi); + + if (!GetConsoleScreenBufferInfoEx (_outputHandle, ref csbi)) + { + throw new Win32Exception (Marshal.GetLastWin32Error ()); + } + + Coord maxWinSize = GetLargestConsoleWindowSize (_outputHandle); + short newCols = Math.Min (cols, maxWinSize.X); + short newRows = Math.Min (rows, maxWinSize.Y); + csbi.dwSize = new Coord (newCols, Math.Max (newRows, (short)1)); + csbi.srWindow = new SmallRect (0, 0, newCols, newRows); + csbi.dwMaximumWindowSize = new Coord (newCols, newRows); + + if (!SetConsoleScreenBufferInfoEx (_outputHandle, ref csbi)) + { + throw new Win32Exception (Marshal.GetLastWin32Error ()); + } + + var winRect = new SmallRect (0, 0, (short)(newCols - 1), (short)Math.Max (newRows - 1, 0)); + + if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect)) + { + //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + return new (cols, rows); + } + + SetConsoleOutputWindow (csbi); + + return new (winRect.Right + 1, newRows - 1 < 0 ? 0 : winRect.Bottom + 1); + } + + private void SetConsoleOutputWindow (CONSOLE_SCREEN_BUFFER_INFOEX csbi) + { + if (_outputHandle != nint.Zero && !SetConsoleScreenBufferInfoEx (_outputHandle, ref csbi)) + { + throw new Win32Exception (Marshal.GetLastWin32Error ()); + } + } + + internal Size GetLargestConsoleWindowSize () + { + Coord maxWinSize = GetLargestConsoleWindowSize (_outputHandle); + + return new (maxWinSize.X, maxWinSize.Y); + } + + // --------------Imports----------------- [DllImport ("kernel32.dll")] private static extern bool GetConsoleMode (nint hConsoleHandle, out uint lpMode); @@ -123,4 +203,103 @@ internal class NetWinVTConsole [DllImport ("kernel32.dll")] private static extern bool SetConsoleMode (nint hConsoleHandle, uint dwMode); + + [DllImport ("kernel32.dll", SetLastError = true)] + private static extern bool GetConsoleScreenBufferInfoEx (nint hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX csbi); + + [DllImport ("kernel32.dll", SetLastError = true)] + private static extern Coord GetLargestConsoleWindowSize ( + nint hConsoleOutput + ); + + [DllImport ("kernel32.dll", SetLastError = true)] + private static extern bool SetConsoleScreenBufferInfoEx (nint hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX consoleScreenBufferInfo); + + [DllImport ("kernel32.dll", SetLastError = true)] + private static extern bool SetConsoleWindowInfo ( + nint hConsoleOutput, + bool bAbsolute, + [In] ref SmallRect lpConsoleWindow + ); + + // -----------structs----------------- + [StructLayout (LayoutKind.Sequential)] + public struct CONSOLE_SCREEN_BUFFER_INFOEX + { + public uint cbSize; + public Coord dwSize; + public Coord dwCursorPosition; + public ushort wAttributes; + public SmallRect srWindow; + public Coord dwMaximumWindowSize; + public ushort wPopupAttributes; + public bool bFullscreenSupported; + + [MarshalAs (UnmanagedType.ByValArray, SizeConst = 16)] + public COLORREF [] ColorTable; + } + + [StructLayout (LayoutKind.Sequential)] + public struct Coord + { + public short X; + public short Y; + + public Coord (short x, short y) + { + X = x; + Y = y; + } + + public readonly override string ToString () { return $"({X},{Y})"; } + } + + [StructLayout (LayoutKind.Sequential)] + public struct SmallRect + { + public short Left; + public short Top; + public short Right; + public short Bottom; + + public SmallRect (short left, short top, short right, short bottom) + { + Left = left; + Top = top; + Right = right; + Bottom = bottom; + } + } + + [StructLayout (LayoutKind.Explicit, Size = 4)] + public struct COLORREF + { + public COLORREF (byte r, byte g, byte b) + { + Value = 0; + R = r; + G = g; + B = b; + } + + public COLORREF (uint value) + { + R = 0; + G = 0; + B = 0; + Value = value & 0x00FFFFFF; + } + + [FieldOffset (0)] + public byte R; + + [FieldOffset (1)] + public byte G; + + [FieldOffset (2)] + public byte B; + + [FieldOffset (0)] + public uint Value; + } }