Fixes issue on restore window size after maximize causing width shrinking

This commit is contained in:
BDisp
2025-07-31 01:14:14 +01:00
parent 1ce27886dc
commit e5edad79f6
3 changed files with 249 additions and 58 deletions

View File

@@ -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);
}

View File

@@ -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
/// <summary>Enqueue a window size event if the window size has changed.</summary>
/// <param name="winHeight"></param>
/// <param name="winWidth"></param>
/// <param name="buffHeight"></param>
/// <param name="buffWidth"></param>
/// <returns></returns>
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)
);

View File

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