Fixes WindowsDriver HeightAsBuffer set to false. (#1466)

This commit is contained in:
BDisp
2021-09-29 22:17:22 +01:00
committed by GitHub
parent f602d3bd1d
commit ef9fb593e5
2 changed files with 239 additions and 146 deletions

View File

@@ -43,7 +43,7 @@ namespace Terminal.Gui {
internal IntPtr InputHandle, OutputHandle;
IntPtr ScreenBuffer;
uint originalConsoleMode;
readonly uint originalConsoleMode;
CursorVisibility? initialCursorVisibility = null;
CursorVisibility? currentCursorVisibility = null;
CursorVisibility? pendingCursorVisibility = null;
@@ -62,10 +62,10 @@ namespace Terminal.Gui {
public CharInfo [] OriginalStdOutChars;
public bool WriteToConsole (CharInfo [] charInfoBuffer, Coord coords, SmallRect window)
public bool WriteToConsole (Size size, CharInfo [] charInfoBuffer, Coord coords, SmallRect window)
{
if (ScreenBuffer == IntPtr.Zero) {
ReadFromConsoleOutput (new Size (Console.WindowWidth, Console.WindowHeight), coords, ref window);
ReadFromConsoleOutput (size, coords, ref window);
}
return WriteConsoleOutput (ScreenBuffer, charInfoBuffer, coords, new Coord () { X = window.Left, Y = window.Top }, ref window);
@@ -92,13 +92,14 @@ namespace Terminal.Gui {
}
if (!SetConsoleActiveScreenBuffer (ScreenBuffer)) {
var err = Marshal.GetLastWin32Error ();
throw new System.ComponentModel.Win32Exception (err);
throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
}
OriginalStdOutChars = new CharInfo [size.Height * size.Width];
ReadConsoleOutput (OutputHandle, OriginalStdOutChars, coords, new Coord () { X = 0, Y = 0 }, ref window);
if (!ReadConsoleOutput (ScreenBuffer, OriginalStdOutChars, coords, new Coord () { X = 0, Y = 0 }, ref window)) {
throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
}
}
public bool SetCursorPosition (Coord position)
@@ -183,6 +184,8 @@ namespace Terminal.Gui {
SetCursorVisibility (initialCursorVisibility.Value);
}
SetConsoleOutputWindow (out _);
ConsoleMode = originalConsoleMode;
//ContinueListeningForConsoleEvents = false;
if (!SetConsoleActiveScreenBuffer (OutputHandle)) {
@@ -196,22 +199,103 @@ namespace Terminal.Gui {
ScreenBuffer = IntPtr.Zero;
}
internal Size GetConsoleBufferWindow (out Point position)
{
if (ScreenBuffer == IntPtr.Zero) {
position = Point.Empty;
return Size.Empty;
}
var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX ();
csbi.cbSize = (uint)Marshal.SizeOf (csbi);
if (!GetConsoleScreenBufferInfoEx (ScreenBuffer, ref csbi)) {
throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
}
var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1,
csbi.srWindow.Bottom - csbi.srWindow.Top + 1);
position = new Point (csbi.srWindow.Left, csbi.srWindow.Top);
return sz;
}
internal Size GetConsoleOutputWindow (out Point position)
{
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 ());
}
var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1,
csbi.srWindow.Bottom - csbi.srWindow.Top + 1);
position = new Point (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 (ScreenBuffer, ref csbi)) {
throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
}
csbi.dwSize = new Coord (cols, Math.Max (rows, (short)1));
csbi.srWindow = new SmallRect (0, 0, cols, rows);
csbi.dwMaximumWindowSize = new Coord (cols, rows);
if (!SetConsoleScreenBufferInfoEx (ScreenBuffer, ref csbi)) {
throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
}
var winRect = new SmallRect (0, 0, (short)(cols - 1), (short)Math.Max (rows - 1, 0));
if (!SetConsoleWindowInfo (ScreenBuffer, true, ref winRect)) {
throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
}
SetConsoleOutputWindow (csbi);
return new Size (winRect.Right + 1, rows - 1 < 0 ? 0 : winRect.Bottom + 1);
}
void SetConsoleOutputWindow (CONSOLE_SCREEN_BUFFER_INFOEX csbi)
{
if (ScreenBuffer != IntPtr.Zero && !SetConsoleScreenBufferInfoEx (OutputHandle, ref csbi)) {
throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
}
}
internal Size SetConsoleOutputWindow (out Point position)
{
if (ScreenBuffer == IntPtr.Zero) {
position = Point.Empty;
return Size.Empty;
}
var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX ();
csbi.cbSize = (uint)Marshal.SizeOf (csbi);
if (!GetConsoleScreenBufferInfoEx (ScreenBuffer, ref csbi)) {
throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
}
var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1,
Math.Max (csbi.srWindow.Bottom - csbi.srWindow.Top + 1, 0));
position = new Point (csbi.srWindow.Left, csbi.srWindow.Top);
SetConsoleOutputWindow (csbi);
var winRect = new SmallRect (0, 0, (short)(sz.Width - 1), (short)Math.Max (sz.Height - 1, 0));
if (!SetConsoleWindowInfo (OutputHandle, true, ref winRect)) {
throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
}
return sz;
}
//bool ContinueListeningForConsoleEvents = true;
public uint ConsoleMode {
get {
uint v;
GetConsoleMode (InputHandle, out v);
GetConsoleMode (InputHandle, out uint v);
return v;
}
set {
SetConsoleMode (InputHandle, value);
}
}
public bool HeightAsBuffer { get; set; }
[Flags]
public enum ConsoleModes : uint {
EnableProcessedInput = 1,
@@ -269,7 +353,7 @@ namespace Terminal.Gui {
[StructLayout (LayoutKind.Explicit)]
public struct MouseEventRecord {
[FieldOffset (0)]
public Coordinate MousePosition;
public Coord MousePosition;
[FieldOffset (4)]
public ButtonState ButtonState;
[FieldOffset (8)]
@@ -283,26 +367,12 @@ namespace Terminal.Gui {
}
}
[StructLayout (LayoutKind.Sequential)]
public struct Coordinate {
public short X;
public short Y;
public Coordinate (short X, short Y)
{
this.X = X;
this.Y = Y;
}
public override string ToString () => $"({X},{Y})";
};
public struct WindowBufferSizeRecord {
public Coordinate size;
public Coord size;
public WindowBufferSizeRecord (short x, short y)
{
this.size = new Coordinate (x, y);
this.size = new Coord (x, y);
}
public override string ToString () => $"[WindowBufferSize{size}";
@@ -413,6 +483,14 @@ namespace Terminal.Gui {
public short Right;
public short Bottom;
public SmallRect (short left, short top, short right, short bottom)
{
Left = left;
Top = top;
Right = right;
Bottom = bottom;
}
public static void MakeEmpty (ref SmallRect rect)
{
rect.Left = -1;
@@ -517,13 +595,12 @@ namespace Terminal.Gui {
DesiredAccess dwDesiredAccess,
ShareMode dwShareMode,
IntPtr secutiryAttributes,
UInt32 flags,
uint flags,
IntPtr screenBufferData
);
internal static IntPtr INVALID_HANDLE_VALUE = new IntPtr (-1);
[DllImport ("kernel32.dll", SetLastError = true)]
static extern bool SetConsoleActiveScreenBuffer (IntPtr Handle);
@@ -531,8 +608,7 @@ namespace Terminal.Gui {
static extern bool GetNumberOfConsoleInputEvents (IntPtr handle, out uint lpcNumberOfEvents);
public uint InputEventCount {
get {
uint v;
GetNumberOfConsoleInputEvents (InputHandle, out v);
GetNumberOfConsoleInputEvents (InputHandle, out uint v);
return v;
}
}
@@ -573,68 +649,80 @@ namespace Terminal.Gui {
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;
// See: https://github.com/migueldeicaza/gui.cs/issues/357
public SMALL_RECT (short Left, short Top, short Right, short Bottom)
{
this.Left = Left;
this.Top = Top;
this.Right = Right;
this.Bottom = Bottom;
}
[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 CONSOLE_SCREEN_BUFFER_INFO {
public int dwSize;
public int dwCursorPosition;
public short wAttributes;
public SMALL_RECT srWindow;
public int dwMaximumWindowSize;
[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;
}
[DllImport ("kernel32.dll", SetLastError = true)]
static extern bool GetConsoleScreenBufferInfo (IntPtr hConsoleOutput, out CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo);
static extern bool GetConsoleScreenBufferInfoEx (IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX csbi);
// Theoretically GetConsoleScreenBuffer height should give the console Windoww size
// It does not work, however, and always returns the size the window was initially created at
internal Size GetWindowSize ()
{
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);
}
#endif
[DllImport ("kernel32.dll", SetLastError = true)]
static extern bool SetConsoleScreenBufferInfoEx (IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX ConsoleScreenBufferInfo);
[DllImport ("kernel32.dll", SetLastError = true)]
static extern bool SetConsoleWindowInfo (
IntPtr hConsoleOutput,
bool bAbsolute,
[In] ref SmallRect lpConsoleWindow);
}
internal class WindowsDriver : ConsoleDriver {
static bool sync = false;
WindowsConsole.CharInfo [] OutputBuffer;
int cols, rows, top;
WindowsConsole winConsole;
int cols, rows, left, top;
WindowsConsole.SmallRect damageRegion;
IClipboard clipboard;
public override int Cols => cols;
public override int Rows => rows;
public override int Left => 0;
public override int Left => left;
public override int Top => top;
public override bool HeightAsBuffer { get; set; }
public override IClipboard Clipboard => clipboard;
public WindowsConsole WinConsole {
get => winConsole;
private set => winConsole = value;
}
public WindowsConsole WinConsole { get; private set; }
Action<KeyEvent> keyHandler;
Action<KeyEvent> keyDownHandler;
@@ -643,7 +731,7 @@ namespace Terminal.Gui {
public WindowsDriver ()
{
winConsole = new WindowsConsole ();
WinConsole = new WindowsConsole ();
clipboard = new WindowsClipboard ();
}
@@ -669,16 +757,18 @@ namespace Terminal.Gui {
{
winChanging = true;
if (!HeightAsBuffer) {
var w = e.Width;
if (w == cols - 3 && e.Height < rows) {
w += 3;
}
var newSize = WinConsole.SetConsoleWindow (
(short)Math.Max (w, 16), (short)e.Height);
left = 0;
top = 0;
cols = e.Width;
rows = e.Height;
cols = newSize.Width;
rows = newSize.Height;
ResizeScreen ();
UpdateOffScreen ();
var bufferCoords = new WindowsConsole.Coord () {
X = (short)Clip.Width,
Y = (short)Clip.Height
};
winConsole.ReadFromConsoleOutput (e, bufferCoords, ref damageRegion);
if (!winChanging) {
TerminalResized.Invoke ();
}
@@ -783,11 +873,17 @@ namespace Terminal.Gui {
break;
case WindowsConsole.EventType.WindowBufferSize:
cols = inputEvent.WindowBufferSizeEvent.size.X;
rows = inputEvent.WindowBufferSizeEvent.size.Y;
ResizeScreen ();
UpdateOffScreen ();
TerminalResized?.Invoke ();
if (HeightAsBuffer) {
var winSize = WinConsole.GetConsoleBufferWindow (out Point pos);
left = pos.X;
top = pos.Y;
cols = inputEvent.WindowBufferSizeEvent.size.X;
rows = inputEvent.WindowBufferSizeEvent.size.Y;
//System.Diagnostics.Debug.WriteLine ($"{HeightAsBuffer},{cols},{rows}");
ResizeScreen ();
UpdateOffScreen ();
TerminalResized?.Invoke ();
}
break;
case WindowsConsole.EventType.Focus:
@@ -1276,11 +1372,10 @@ namespace Terminal.Gui {
{
TerminalResized = terminalResized;
cols = Console.WindowWidth;
rows = Console.WindowHeight;
#if false
winConsole.ShowWindow (WindowsConsole.RESTORE);
#endif
var winSize = WinConsole.GetConsoleOutputWindow (out Point pos);
cols = winSize.Width;
rows = winSize.Height;
WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
ResizeScreen ();
@@ -1333,7 +1428,7 @@ namespace Terminal.Gui {
Bottom = (short)Rows,
Right = (short)Cols
};
winConsole.ForceRefreshCursorVisibility ();
WinConsole.ForceRefreshCursorVisibility ();
}
void UpdateOffScreen ()
@@ -1415,7 +1510,7 @@ namespace Terminal.Gui {
{
UpdateScreen ();
winConsole.SetInitialCursorVisibility ();
WinConsole.SetInitialCursorVisibility ();
#if false
var bufferCoords = new WindowsConsole.Coord (){
X = (short)Clip.Width,
@@ -1430,7 +1525,7 @@ namespace Terminal.Gui {
};
UpdateCursor();
winConsole.WriteToConsole (OutputBuffer, bufferCoords, window);
WinConsole.WriteToConsole (OutputBuffer, bufferCoords, window);
#endif
}
@@ -1452,8 +1547,8 @@ namespace Terminal.Gui {
//};
UpdateCursor ();
winConsole.WriteToConsole (OutputBuffer, bufferCoords, damageRegion);
// System.Diagnostics.Debugger.Log(0, "debug", $"Region={damageRegion.Right - damageRegion.Left},{damageRegion.Bottom - damageRegion.Top}\n");
WinConsole.WriteToConsole (new Size (Cols, Rows), OutputBuffer, bufferCoords, damageRegion);
// System.Diagnostics.Debugger.Log (0, "debug", $"Region={damageRegion.Right - damageRegion.Left},{damageRegion.Bottom - damageRegion.Top}\n");
WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
}
@@ -1463,12 +1558,13 @@ namespace Terminal.Gui {
X = (short)ccol,
Y = (short)crow
};
winConsole.SetCursorPosition (position);
WinConsole.SetCursorPosition (position);
}
public override void End ()
{
winConsole.Cleanup ();
WinConsole.Cleanup ();
WinConsole = null;
}
public override Attribute GetAttribute ()
@@ -1479,28 +1575,30 @@ namespace Terminal.Gui {
/// <inheritdoc/>
public override bool GetCursorVisibility (out CursorVisibility visibility)
{
return winConsole.GetCursorVisibility (out visibility);
return WinConsole.GetCursorVisibility (out visibility);
}
/// <inheritdoc/>
public override bool SetCursorVisibility (CursorVisibility visibility)
{
return winConsole.SetCursorVisibility (visibility);
return WinConsole.SetCursorVisibility (visibility);
}
/// <inheritdoc/>
public override bool EnsureCursorVisibility ()
{
return winConsole.EnsureCursorVisibility ();
return WinConsole.EnsureCursorVisibility ();
}
public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control)
{
WindowsConsole.InputRecord input = new WindowsConsole.InputRecord ();
input.EventType = WindowsConsole.EventType.Key;
WindowsConsole.InputRecord input = new WindowsConsole.InputRecord {
EventType = WindowsConsole.EventType.Key
};
WindowsConsole.KeyEventRecord keyEvent = new WindowsConsole.KeyEventRecord ();
keyEvent.bKeyDown = true;
WindowsConsole.KeyEventRecord keyEvent = new WindowsConsole.KeyEventRecord {
bKeyDown = true
};
WindowsConsole.ControlKeyState controlKey = new WindowsConsole.ControlKeyState ();
if (shift) {
controlKey |= WindowsConsole.ControlKeyState.ShiftPressed;
@@ -1607,12 +1705,12 @@ namespace Terminal.Gui {
internal class WindowsMainLoop : IMainLoopDriver {
ManualResetEventSlim eventReady = new ManualResetEventSlim (false);
ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
//ManualResetEventSlim winChange = new ManualResetEventSlim (false);
ManualResetEventSlim winChange = new ManualResetEventSlim (false);
MainLoop mainLoop;
ConsoleDriver consoleDriver;
WindowsConsole winConsole;
//bool winChanged;
//Size windowSize;
bool winChanged;
Size windowSize;
CancellationTokenSource tokenSource = new CancellationTokenSource ();
// The records that we keep fetching
@@ -1630,10 +1728,7 @@ namespace Terminal.Gui {
public WindowsMainLoop (ConsoleDriver consoleDriver = null)
{
if (consoleDriver == null) {
throw new ArgumentNullException ("Console driver instance must be provided.");
}
this.consoleDriver = consoleDriver;
this.consoleDriver = consoleDriver ?? throw new ArgumentNullException ("Console driver instance must be provided.");
winConsole = ((WindowsDriver)consoleDriver).WinConsole;
}
@@ -1641,8 +1736,7 @@ namespace Terminal.Gui {
{
this.mainLoop = mainLoop;
Task.Run (WindowsInputHandler);
// Not working yet.
//Task.Run (CheckWinChange);
Task.Run (CheckWinChange);
}
void WindowsInputHandler ()
@@ -1657,29 +1751,31 @@ namespace Terminal.Gui {
}
}
//void CheckWinChange ()
//{
// while (true) {
// winChange.Wait ();
// winChange.Reset ();
// WaitWinChange ();
// winChanged = true;
// eventReady.Set ();
// }
//}
void CheckWinChange ()
{
while (true) {
winChange.Wait ();
winChange.Reset ();
WaitWinChange ();
winChanged = true;
eventReady.Set ();
}
}
//void WaitWinChange ()
//{
// while (true) {
// if (!consoleDriver.HeightAsBuffer) {
// windowSize = new Size (Console.WindowWidth, Console.WindowHeight);
// if (windowSize.Height < consoleDriver.Rows) {
// // I still haven't been able to find a way to capture the shrinking in height.
// //return;
// }
// }
// }
//}
void WaitWinChange ()
{
while (true) {
Thread.Sleep (100);
if (!consoleDriver.HeightAsBuffer) {
windowSize = winConsole.GetConsoleBufferWindow (out _);
//System.Diagnostics.Debug.WriteLine ($"{consoleDriver.HeightAsBuffer},{windowSize.Width},{windowSize.Height}");
if (windowSize != Size.Empty && windowSize.Width != consoleDriver.Cols
|| windowSize.Height != consoleDriver.Rows) {
return;
}
}
}
}
void IMainLoopDriver.Wakeup ()
{
@@ -1695,8 +1791,7 @@ namespace Terminal.Gui {
result = null;
waitForProbe.Set ();
// Nor working yet.
//winChange.Set ();
winChange.Set ();
try {
if (!tokenSource.IsCancellationRequested) {
@@ -1709,8 +1804,7 @@ namespace Terminal.Gui {
}
if (!tokenSource.IsCancellationRequested) {
//return result != null || CheckTimers (wait, out _) || winChanged;
return result != null || CheckTimers (wait, out _);
return result != null || CheckTimers (wait, out _) || winChanged;
}
tokenSource.Dispose ();
@@ -1748,10 +1842,10 @@ namespace Terminal.Gui {
result = null;
ProcessInput?.Invoke (inputEvent);
}
//if (winChanged) {
// winChanged = false;
// WinChanged?.Invoke (windowSize);
//}
if (winChanged) {
winChanged = false;
WinChanged?.Invoke (windowSize);
}
}
}

View File

@@ -118,11 +118,9 @@ namespace Terminal.Gui {
if (Driver == null) {
throw new ArgumentNullException ("The driver must be initialized first.");
}
if (Driver.HeightAsBuffer != value) {
Driver.HeightAsBuffer = value;
}
}
}
/// <summary>
/// Used only by <see cref="NetDriver"/> to forcing always moving the cursor position when writing to the screen.
@@ -545,6 +543,7 @@ namespace Terminal.Gui {
if (OutsideFrame (new Point (nme.X, nme.Y), mouseGrabView.Frame)) {
lastMouseOwnerView?.OnMouseLeave (me);
}
// System.Diagnostics.Debug.WriteLine ($"{nme.Flags};{nme.X};{nme.Y};{mouseGrabView}");
if (mouseGrabView != null) {
mouseGrabView.OnMouseEvent (nme);
return;