Fixes #4200. ExtendedCharInfo needs be enhanced to properly deal with all codepoints (#4202)

* Fixes #4196. Application.Begin doesn't refresh the screen at start

* Fixes #4198. Application.Invoke isn't wakeup the driver if idle

* Reformatting to run CI again

* Revert "Reformatting to run CI again"

This reverts commit ef639c1e64.

* Trying fix an issue where sometimes subview variable is null running unit tests

* Replace ExtendedCharInfo.Char with char array

* Replace IsWindowsTerminal with IsVirtualTerminal

* Add a lastSize parameter to process resize automatically

* Handling surrogate pairs in input

* Implement SetConsoleTextAttribute

* Prevent select true color is not supported

* Fix null exception

* Revert GetWindowSize and add SetWindowSize

* Fix unit tests

* Revert all v2 changes except the one related with the ExtendedCharInfo

* Revert newlines and FakeOutput

* Prevents null reference

* Add gnome-terminal to launch settings

* Fixes issue on restore window size after maximize causing width shrinking

* Add ; exec bash to stay in terminal

* Fixes issue on restore window size after maximize causing width shrinking

* Tidying up input and output console modes

* Fixes uninitialized screen buffer.

* Revert "Fixes issue on restore window size after maximize causing width shrinking"

This reverts commit e5edad79f6.

* Reset console after sending escape sequences

* Remove unnecessary code only for buggy VSDebugConsole

* Fix more annoying exceptions

* Ensure flush the input buffer before reset the console

* Remove unnecessary ENABLE_VIRTUAL_TERMINAL_INPUT

* Remove unnecessary error handles

* Fix CI warnings

* Fix more CI warnings

* Fix more CI warnings

* Fixes #2796. CursesDriver doesn't render wide codepoints correctly

---------

Co-authored-by: Tig <tig@users.noreply.github.com>
This commit is contained in:
BDisp
2025-09-11 20:37:03 +01:00
committed by GitHub
parent 6b7855e6b8
commit ad8ebc9890
19 changed files with 514 additions and 472 deletions

View File

@@ -48,6 +48,30 @@
"commandLineArgs": "dotnet UICatalog.dll --driver v2net",
"distributionName": ""
},
"WSL-Gnome: UICatalog": {
"commandName": "Executable",
"executablePath": "wsl",
"commandLineArgs": "bash -c 'while [ ! -e \"$XDG_RUNTIME_DIR/bus\" ]; do sleep 0.1; done; gnome-terminal --wait -- bash -l -c \"dotnet UICatalog.dll; exec bash\"'",
"distributionName": ""
},
"WSL-Gnome: UICatalog --driver NetDriver": {
"commandName": "Executable",
"executablePath": "wsl",
"commandLineArgs": "bash -c 'while [ ! -e \"$XDG_RUNTIME_DIR/bus\" ]; do sleep 0.1; done; gnome-terminal --wait -- bash -l -c \"dotnet UICatalog.dll --driver NetDriver; exec bash\"'",
"distributionName": ""
},
"WSL-Gnome: UICatalog --driver v2": {
"commandName": "Executable",
"executablePath": "wsl",
"commandLineArgs": "bash -c 'while [ ! -e \"$XDG_RUNTIME_DIR/bus\" ]; do sleep 0.1; done; gnome-terminal --wait -- bash -l -c \"dotnet UICatalog.dll --driver v2; exec bash\"'",
"distributionName": ""
},
"WSL-Gnome: UICatalog --driver v2net": {
"commandName": "Executable",
"executablePath": "wsl",
"commandLineArgs": "bash -c 'while [ ! -e \"$XDG_RUNTIME_DIR/bus\" ]; do sleep 0.1; done; gnome-terminal --wait -- bash -l -c \"dotnet UICatalog.dll --driver v2net; exec bash\"'",
"distributionName": ""
},
"Benchmark All": {
"commandName": "Project",
"commandLineArgs": "--benchmark"

View File

@@ -252,7 +252,7 @@ internal class NumericUpDownEditor<T> : View where T : notnull
{
X = Pos.Center (),
Y = Pos.Bottom (_increment) + 1,
Increment = NumericUpDown<int>.TryConvert (1, out T? increment) ? increment : default,
Increment = NumericUpDown<int>.TryConvert (1, out T? increment) ? increment : default (T?),
};
_numericUpDown.ValueChanged += NumericUpDownOnValueChanged;

View File

@@ -166,6 +166,16 @@ public class UICatalogTop : Toplevel
CheckedState = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked
};
_force16ColorsMenuItemCb.CheckedStateChanging += (sender, args) =>
{
if (Application.Force16Colors
&& args.Result == CheckState.UnChecked
&& !Application.Driver!.SupportsTrueColor)
{
args.Handled = true;
}
};
_force16ColorsMenuItemCb.CheckedStateChanged += (sender, args) =>
{
Application.Force16Colors = args.Value == CheckState.Checked;
@@ -609,8 +619,19 @@ public class UICatalogTop : Toplevel
_force16ColorsShortcutCb.CheckedStateChanging += (sender, args) =>
{
Application.Force16Colors = args.Result == CheckState.Checked;
_force16ColorsMenuItemCb!.CheckedState = args.Result;
if (Application.Force16Colors
&& args.Result == CheckState.UnChecked
&& !Application.Driver!.SupportsTrueColor)
{
// If the driver does not support TrueColor, we cannot disable 16 colors
args.Handled = true;
}
};
_force16ColorsShortcutCb.CheckedStateChanged += (sender, args) =>
{
Application.Force16Colors = args.Value == CheckState.Checked;
_force16ColorsMenuItemCb!.CheckedState = args.Value;
Application.LayoutAndDraw ();
};

View File

@@ -20,7 +20,7 @@ public class SixelSupportDetector
public void Detect (Action<SixelSupportResult> resultCallback)
{
var result = new SixelSupportResult ();
result.SupportsTransparency = IsWindowsTerminal () || IsXtermWithTransparency ();
result.SupportsTransparency = IsVirtualTerminal () || IsXtermWithTransparency ();
IsSixelSupportedByDar (result, resultCallback);
}
@@ -142,7 +142,7 @@ public class SixelSupportDetector
private static bool ResponseIndicatesSupport (string response) { return response.Split (';').Contains ("4"); }
private static bool IsWindowsTerminal ()
private static bool IsVirtualTerminal ()
{
return !string.IsNullOrWhiteSpace (Environment.GetEnvironmentVariable ("WT_SESSION"));

View File

@@ -269,7 +269,7 @@ public abstract class ConsoleDriver : IConsoleDriver
if (Contents [Row, Col - 1].Rune.GetColumns () > 1)
{
// Invalidate cell to left
Contents [Row, Col - 1].Rune = Rune.ReplacementChar;
Contents [Row, Col - 1].Rune = (Rune)'\0';
Contents [Row, Col - 1].IsDirty = true;
}
}
@@ -308,7 +308,7 @@ public abstract class ConsoleDriver : IConsoleDriver
{
// Invalidate cell to right so that it doesn't get drawn
// TODO: Figure out if it is better to show a replacement character or ' '
Contents [Row, Col + 1].Rune = Rune.ReplacementChar;
Contents [Row, Col + 1].Rune = (Rune)'\0';
Contents [Row, Col + 1].IsDirty = true;
}
}

View File

@@ -123,12 +123,6 @@ internal class CursesDriver : ConsoleDriver
if (!RunningUnitTests)
{
Platform.Suspend ();
if (Force16Colors)
{
Curses.Window.Standard.redrawwin ();
Curses.refresh ();
}
}
StartReportingMouseMoves ();
@@ -139,90 +133,17 @@ internal class CursesDriver : ConsoleDriver
EnsureCursorVisibility ();
if (!RunningUnitTests && Col >= 0 && Col < Cols && Row >= 0 && Row < Rows)
{
if (Force16Colors)
{
Curses.move (Row, Col);
Curses.raw ();
Curses.noecho ();
Curses.refresh ();
}
else
{
_mainLoopDriver?.WriteRaw (EscSeqUtils.CSI_SetCursorPosition (Row + 1, Col + 1));
}
}
}
public override bool UpdateScreen ()
{
bool updated = false;
if (Force16Colors)
{
for (var row = 0; row < Rows; row++)
{
if (!_dirtyLines! [row])
{
continue;
}
_dirtyLines [row] = false;
for (var col = 0; col < Cols; col++)
{
if (Contents! [row, col].IsDirty == false)
{
continue;
}
if (RunningUnitTests)
{
// In unit tests, we don't want to actually write to the screen.
continue;
}
Curses.attrset (Contents [row, col].Attribute.GetValueOrDefault ().PlatformColor);
Rune rune = Contents [row, col].Rune;
if (rune.IsBmp)
{
// BUGBUG: CursesDriver doesn't render CharMap correctly for wide chars (and other Unicode) - Curses is doing something funky with glyphs that report GetColums() of 1 yet are rendered wide. E.g. 0x2064 (invisible times) is reported as 1 column but is rendered as 2. WindowsDriver & NetDriver correctly render this as 1 column, overlapping the next cell.
if (rune.GetColumns () < 2)
{
Curses.mvaddch (row, col, rune.Value);
}
else /*if (col + 1 < Cols)*/
{
Curses.mvaddwstr (row, col, rune.ToString ());
}
}
else
{
Curses.mvaddwstr (row, col, rune.ToString ());
if (rune.GetColumns () > 1 && col + 1 < Cols)
{
// TODO: This is a hack to deal with non-BMP and wide characters.
//col++;
Curses.mvaddch (row, ++col, '*');
}
}
}
}
if (!RunningUnitTests)
{
Curses.move (Row, Col);
_window?.wrefresh ();
}
}
else
{
if (RunningUnitTests
|| Console.WindowHeight < 1
|| Contents!.Length != Rows * Cols
|| Contents?.Length != Rows * Cols
|| Rows != Console.WindowHeight)
{
return updated;
@@ -256,6 +177,7 @@ internal class CursesDriver : ConsoleDriver
return updated;
}
updated = true;
_dirtyLines [row] = false;
output.Clear ();
@@ -266,7 +188,6 @@ internal class CursesDriver : ConsoleDriver
for (; col < cols; col++)
{
updated = true;
if (!Contents [row, col].IsDirty)
{
if (output.Length > 0)
@@ -298,6 +219,13 @@ internal class CursesDriver : ConsoleDriver
{
redrawAttr = attr;
if (Force16Colors)
{
output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Foreground.GetAnsiColorCode ()));
output.Append (EscSeqUtils.CSI_SetBackgroundColor (attr.Background.GetAnsiColorCode ()));
}
else
{
output.Append (
EscSeqUtils.CSI_SetForegroundColorRGB (
attr.Foreground.R,
@@ -314,6 +242,7 @@ internal class CursesDriver : ConsoleDriver
)
);
}
}
outputWidth++;
Rune rune = Contents [row, col].Rune;
@@ -347,14 +276,16 @@ internal class CursesDriver : ConsoleDriver
SetCursorPosition (lastCol, row);
Console.Write (output);
}
}
// SIXELS
foreach (SixelToRender s in Application.Sixel)
foreach (var s in Application.Sixel)
{
if (!string.IsNullOrWhiteSpace (s.SixelData))
{
SetCursorPosition (s.ScreenPosition.X, s.ScreenPosition.Y);
Console.Write (s.SixelData);
}
}
}
SetCursorPosition (0, 0);
@@ -368,7 +299,6 @@ internal class CursesDriver : ConsoleDriver
lastCol += outputWidth;
outputWidth = 0;
}
}
return updated;
}
@@ -396,29 +326,6 @@ internal class CursesDriver : ConsoleDriver
);
}
/// <inheritdoc/>
/// <remarks>
/// In the CursesDriver, colors are encoded as an int. The foreground color is stored in the most significant 4
/// bits, and the background color is stored in the least significant 4 bits. The Terminal.GUi Color values are
/// converted to curses color encoding before being encoded.
/// </remarks>
public override Attribute MakeColor (in Color foreground, in Color background)
{
if (!RunningUnitTests && Force16Colors)
{
return MakeColor (
ColorNameToCursesColorNumber (foreground.GetClosestNamedColor16 ()),
ColorNameToCursesColorNumber (background.GetClosestNamedColor16 ())
);
}
return new (
0,
foreground,
background
);
}
private static short ColorNameToCursesColorNumber (ColorName16 color)
{
switch (color)

View File

@@ -223,7 +223,7 @@ internal class NetDriver : ConsoleDriver
// BUGBUG: Fix this nullable issue.
/// <inheritdoc />
internal override IAnsiResponseParser GetParser () => _mainLoopDriver._netEvents.Parser;
internal override IAnsiResponseParser GetParser () => _mainLoopDriver!._netEvents!.Parser;
internal NetMainLoop? _mainLoopDriver;
/// <inheritdoc />
@@ -356,11 +356,6 @@ internal class NetDriver : ConsoleDriver
}
public override void End ()
{
if (IsWinPlatform)
{
NetWinConsole?.Cleanup ();
}
StopReportingMouseMoves ();
if (!RunningUnitTests)
@@ -373,6 +368,11 @@ internal class NetDriver : ConsoleDriver
//Set cursor key to cursor.
Console.Out.Write (EscSeqUtils.CSI_ShowCursor);
Console.Out.Close ();
// Reset the console to its original state
// after sending the escape sequences to restore
// alternative buffer and cursor visibility.
NetWinConsole?.Cleanup ();
}
}
@@ -746,47 +746,6 @@ internal class NetDriver : ConsoleDriver
public virtual 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

@@ -80,7 +80,7 @@ internal class NetEvents : IDisposable
return Console.ReadKey (intercept);
}
while (!_netEventsDisposed.IsCancellationRequested)
while (!_netEventsDisposed!.IsCancellationRequested)
{
Task.Delay (100, _netEventsDisposed.Token).Wait (_netEventsDisposed.Token);
@@ -113,11 +113,11 @@ internal class NetEvents : IDisposable
private void ProcessInputQueue ()
{
while (!_netEventsDisposed.IsCancellationRequested)
while (_netEventsDisposed is { IsCancellationRequested: false })
{
if (_inputQueue.Count == 0)
{
while (!_netEventsDisposed.IsCancellationRequested)
while (_netEventsDisposed is { IsCancellationRequested: false })
{
ConsoleKeyInfo consoleKeyInfo;
@@ -147,7 +147,7 @@ internal class NetEvents : IDisposable
{
void RequestWindowSize ()
{
while (!_netEventsDisposed.IsCancellationRequested)
while (_netEventsDisposed is { IsCancellationRequested: false })
{
// Wait for a while then check if screen has changed sizes
Task.Delay (500, _netEventsDisposed.Token).Wait (_netEventsDisposed.Token);
@@ -179,7 +179,7 @@ internal class NetEvents : IDisposable
_netEventsDisposed.Token.ThrowIfCancellationRequested ();
}
while (!_netEventsDisposed.IsCancellationRequested)
while (!_netEventsDisposed!.IsCancellationRequested)
{
try
{
@@ -434,10 +434,6 @@ internal class NetEvents : IDisposable
new InputResult { EventType = eventType, WindowPositionEvent = winPositionEv }
);
}
else
{
return;
}
break;
@@ -563,7 +559,7 @@ internal class NetEvents : IDisposable
public readonly override string ToString ()
{
return EventType switch
return (EventType switch
{
EventType.Key => ToString (ConsoleKeyInfo),
EventType.Mouse => MouseEvent.ToString (),
@@ -571,7 +567,7 @@ internal class NetEvents : IDisposable
//EventType.WindowSize => WindowSize.ToString (),
//EventType.RequestResponse => RequestResponse.ToString (),
_ => "Unknown event type: " + EventType
};
})!;
}
/// <summary>Prints a ConsoleKeyInfoEx structure</summary>

View File

@@ -5,31 +5,31 @@ namespace Terminal.Gui.Drivers;
internal class NetWinVTConsole
{
private const uint DISABLE_NEWLINE_AUTO_RETURN = 8;
private const uint ENABLE_ECHO_INPUT = 4;
private const uint ENABLE_EXTENDED_FLAGS = 128;
private const uint ENABLE_INSERT_MODE = 32;
private const uint ENABLE_LINE_INPUT = 2;
private const uint ENABLE_LVB_GRID_WORLDWIDE = 10;
private const uint ENABLE_MOUSE_INPUT = 16;
// Input modes.
private const uint ENABLE_PROCESSED_INPUT = 1;
private const uint ENABLE_LINE_INPUT = 2;
private const uint ENABLE_ECHO_INPUT = 4;
private const uint ENABLE_WINDOW_INPUT = 8;
private const uint ENABLE_MOUSE_INPUT = 16;
private const uint ENABLE_INSERT_MODE = 32;
private const uint ENABLE_QUICK_EDIT_MODE = 64;
private const uint ENABLE_EXTENDED_FLAGS = 128;
private const uint ENABLE_VIRTUAL_TERMINAL_INPUT = 512;
// Output modes.
private const uint ENABLE_PROCESSED_OUTPUT = 1;
private const uint ENABLE_QUICK_EDIT_MODE = 64;
private const uint ENABLE_VIRTUAL_TERMINAL_INPUT = 512;
private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4;
private const uint ENABLE_WINDOW_INPUT = 8;
private const uint ENABLE_WRAP_AT_EOL_OUTPUT = 2;
private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4;
private const uint DISABLE_NEWLINE_AUTO_RETURN = 8;
private const uint ENABLE_LVB_GRID_WORLDWIDE = 10;
// Standard handles.
private const int STD_ERROR_HANDLE = -12;
private const int STD_INPUT_HANDLE = -10;
private const int STD_OUTPUT_HANDLE = -11;
private readonly nint _errorHandle;
// Handles and original console modes.
private readonly nint _inputHandle;
private readonly uint _originalErrorConsoleMode;
private readonly uint _originalInputConsoleMode;
private readonly uint _originalOutputConsoleMode;
private readonly nint _outputHandle;
@@ -45,7 +45,7 @@ internal class NetWinVTConsole
_originalInputConsoleMode = mode;
if ((mode & ENABLE_VIRTUAL_TERMINAL_INPUT) < ENABLE_VIRTUAL_TERMINAL_INPUT)
if ((mode & ENABLE_VIRTUAL_TERMINAL_INPUT) == 0)
{
mode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
@@ -64,34 +64,15 @@ internal class NetWinVTConsole
_originalOutputConsoleMode = mode;
if ((mode & (ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN)
if ((mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0)
{
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
if (!SetConsoleMode (_outputHandle, mode))
{
throw new ApplicationException ($"Failed to set output console mode, error code: {GetLastError ()}.");
}
}
_errorHandle = GetStdHandle (STD_ERROR_HANDLE);
if (!GetConsoleMode (_errorHandle, out mode))
{
throw new ApplicationException ($"Failed to get error console mode, error code: {GetLastError ()}.");
}
_originalErrorConsoleMode = mode;
if ((mode & DISABLE_NEWLINE_AUTO_RETURN) < DISABLE_NEWLINE_AUTO_RETURN)
{
mode |= DISABLE_NEWLINE_AUTO_RETURN;
if (!SetConsoleMode (_errorHandle, mode))
{
throw new ApplicationException ($"Failed to set error console mode, error code: {GetLastError ()}.");
}
}
}
public void Cleanup ()
@@ -110,11 +91,6 @@ internal class NetWinVTConsole
{
throw new ApplicationException ($"Failed to restore output console mode, error code: {GetLastError ()}.");
}
if (!SetConsoleMode (_errorHandle, _originalErrorConsoleMode))
{
throw new ApplicationException ($"Failed to restore error console mode, error code: {GetLastError ()}.");
}
}
[DllImport ("kernel32.dll")]

View File

@@ -234,7 +234,7 @@ internal partial class WindowsOutput : OutputBase, IConsoleOutput
public override void Write (IOutputBuffer outputBuffer)
{
_force16Colors = Application.Driver!.Force16Colors;
_everythingStringBuilder = new StringBuilder ();
_everythingStringBuilder.Clear ();
// for 16 color mode we will write to a backing buffer then flip it to the active one at the end to avoid jitter.
_consoleBuffer = 0;
@@ -485,7 +485,7 @@ internal partial class WindowsOutput : OutputBase, IConsoleOutput
private bool _isDisposed;
private bool _force16Colors;
private nint _consoleBuffer;
private StringBuilder _everythingStringBuilder;
private StringBuilder _everythingStringBuilder = new ();
/// <inheritdoc/>
public void Dispose ()

View File

@@ -2,6 +2,8 @@
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Runtime.InteropServices;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#pragma warning disable IDE1006// Naming rule violation: Prefix '_' is not expected
namespace Terminal.Gui.Drivers;
@@ -35,10 +37,69 @@ public partial class WindowsConsole
newConsoleMode &= ~(uint)ConsoleModes.EnableProcessedInput;
ConsoleMode = newConsoleMode;
IsVirtualTerminal = GetConsoleMode (_outputHandle, out uint mode) && (mode & (uint)ConsoleModes.EnableVirtualTerminalProcessing) != 0;
if (!IsVirtualTerminal)
{
CreateConsoleScreenBuffer ();
Size bufferSize = GetConsoleBufferWindow (out _);
SmallRect window = new ()
{
Top = 0,
Left = 0,
Bottom = (short)bufferSize.Height,
Right = (short)bufferSize.Width
};
ReadFromConsoleOutput (bufferSize, new ((short)bufferSize.Width, (short)bufferSize.Height), ref window);
if (!GetConsoleMode (_screenBuffer, out mode))
{
throw new ApplicationException ($"Failed to get screenBuffer console mode, error code: {Marshal.GetLastWin32Error ()}.");
}
const uint ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002;
mode &= ~ENABLE_WRAP_AT_EOL_OUTPUT; // Disable wrap
if (!SetConsoleMode (_screenBuffer, mode))
{
throw new ApplicationException ($"Failed to set screenBuffer console mode, error code: {Marshal.GetLastWin32Error ()}.");
}
}
SetInitialCursorVisibility ();
_inputReadyCancellationTokenSource = new ();
Task.Run (ProcessInputQueue, _inputReadyCancellationTokenSource.Token);
}
private void CreateConsoleScreenBuffer ()
{
_screenBuffer = CreateConsoleScreenBuffer (
DesiredAccess.GenericRead | DesiredAccess.GenericWrite,
ShareMode.FileShareRead | ShareMode.FileShareWrite,
nint.Zero,
1,
nint.Zero
);
if (_screenBuffer == INVALID_HANDLE_VALUE)
{
int err = Marshal.GetLastWin32Error ();
if (err != 0)
{
throw new Win32Exception (err);
}
}
if (!SetConsoleActiveScreenBuffer (_screenBuffer))
{
throw new Win32Exception (Marshal.GetLastWin32Error ());
}
}
public InputRecord? DequeueInput ()
{
while (_inputReadyCancellationTokenSource is { })
@@ -146,35 +207,138 @@ public partial class WindowsConsole
private CharInfo []? _originalStdOutChars;
private struct Run
{
public ushort attr;
public string text;
public Run (ushort attr, string text)
{
this.attr = attr;
this.text = text;
}
}
public bool WriteToConsole (Size size, ExtendedCharInfo [] charInfoBuffer, Coord bufferSize, SmallRect window, bool force16Colors)
{
//Debug.WriteLine ("WriteToConsole");
if (!IsWindowsTerminal && _screenBuffer == nint.Zero)
{
ReadFromConsoleOutput (size, bufferSize, ref window);
}
SetInitialCursorVisibility ();
Attribute? prev = null;
var result = false;
if (force16Colors)
{
_stringBuilder.Clear ();
var i = 0;
CharInfo [] ci = new CharInfo [charInfoBuffer.Length];
List<Run> runs = [];
Run? current = null;
SetCursorPosition (new Coord (0, 0));
foreach (ExtendedCharInfo info in charInfoBuffer)
{
ci [i++] = new CharInfo
if (IsVirtualTerminal)
{
Char = new CharUnion { UnicodeChar = info.Char },
Attributes =
(ushort)((int)info.Attribute.Foreground.GetClosestNamedColor16 () | ((int)info.Attribute.Background.GetClosestNamedColor16 () << 4))
};
Attribute attr = info.Attribute;
AnsiColorCode fgColor = info.Attribute.Foreground.GetAnsiColorCode ();
AnsiColorCode bgColor = info.Attribute.Background.GetAnsiColorCode ();
if (attr != prev)
{
prev = attr;
_stringBuilder.Append (EscSeqUtils.CSI_SetForegroundColor (fgColor));
_stringBuilder.Append (EscSeqUtils.CSI_SetBackgroundColor (bgColor));
EscSeqUtils.CSI_AppendTextStyleChange (_stringBuilder, _redrawTextStyle, attr.Style);
_redrawTextStyle = attr.Style;
}
result = WriteConsoleOutput (IsWindowsTerminal ? _outputHandle : _screenBuffer, ci, bufferSize, new Coord { X = window.Left, Y = window.Top }, ref window);
if (info.Char [0] != '\x1b')
{
if (!info.Empty)
{
_stringBuilder.Append (info.Char);
}
}
else
{
_stringBuilder.Append (' ');
}
}
else
{
if (info.Empty)
{
i++;
continue;
}
if (!info.Empty)
{
var attr = (ushort)((int)info.Attribute.Foreground.GetClosestNamedColor16 ()
| ((int)info.Attribute.Background.GetClosestNamedColor16 () << 4));
// Start new run if needed
if (current == null || attr != current.Value.attr)
{
if (current != null)
{
runs.Add (new (current.Value.attr, _stringBuilder.ToString ()));
}
_stringBuilder.Clear ();
current = new Run (attr, "");
}
_stringBuilder!.Append (info.Char);
}
i++;
if (i > 0 && i <= charInfoBuffer.Length && i % bufferSize.X == 0)
{
if (i < charInfoBuffer.Length)
{
_stringBuilder.AppendLine ();
}
runs.Add (new (current!.Value.attr, _stringBuilder.ToString ()));
_stringBuilder.Clear ();
}
}
}
if (IsVirtualTerminal)
{
_stringBuilder.Append (EscSeqUtils.CSI_RestoreCursorPosition);
_stringBuilder.Append (EscSeqUtils.CSI_HideCursor);
var s = _stringBuilder.ToString ();
// TODO: requires extensive testing if we go down this route
// If console output has changed
if (s != _lastWrite)
{
// supply console with the new content
result = WriteConsole (_outputHandle, s, (uint)s.Length, out uint _, nint.Zero);
}
_lastWrite = s;
foreach (var sixel in Application.Sixel)
{
SetCursorPosition (new Coord ((short)sixel.ScreenPosition.X, (short)sixel.ScreenPosition.Y));
WriteConsole (IsVirtualTerminal ? _outputHandle : _screenBuffer, sixel.SixelData, (uint)sixel.SixelData.Length, out uint _, nint.Zero);
}
}
else
{
foreach (var run in runs)
{
SetConsoleTextAttribute (IsVirtualTerminal ? _outputHandle : _screenBuffer, run.attr);
result = WriteConsole (IsVirtualTerminal ? _outputHandle : _screenBuffer, run.text, (uint)run.text.Length, out _, nint.Zero);
}
}
}
else
{
@@ -183,8 +347,6 @@ public partial class WindowsConsole
_stringBuilder.Append (EscSeqUtils.CSI_SaveCursorPosition);
EscSeqUtils.CSI_AppendCursorPosition (_stringBuilder, 0, 0);
Attribute? prev = null;
foreach (ExtendedCharInfo info in charInfoBuffer)
{
Attribute attr = info.Attribute;
@@ -198,7 +360,7 @@ public partial class WindowsConsole
_redrawTextStyle = attr.Style;
}
if (info.Char != '\x1b')
if (info.Char [0] != '\x1b')
{
if (!info.Empty)
{
@@ -229,7 +391,7 @@ public partial class WindowsConsole
foreach (var sixel in Application.Sixel)
{
SetCursorPosition (new Coord ((short)sixel.ScreenPosition.X, (short)sixel.ScreenPosition.Y));
WriteConsole (IsWindowsTerminal ? _outputHandle : _screenBuffer, sixel.SixelData, (uint)sixel.SixelData.Length, out uint _, nint.Zero);
WriteConsole (IsVirtualTerminal ? _outputHandle : _screenBuffer, sixel.SixelData, (uint)sixel.SixelData.Length, out uint _, nint.Zero);
}
}
@@ -259,29 +421,6 @@ public partial class WindowsConsole
public void ReadFromConsoleOutput (Size size, Coord coords, ref SmallRect window)
{
_screenBuffer = CreateConsoleScreenBuffer (
DesiredAccess.GenericRead | DesiredAccess.GenericWrite,
ShareMode.FileShareRead | ShareMode.FileShareWrite,
nint.Zero,
1,
nint.Zero
);
if (_screenBuffer == INVALID_HANDLE_VALUE)
{
int err = Marshal.GetLastWin32Error ();
if (err != 0)
{
throw new Win32Exception (err);
}
}
if (!SetConsoleActiveScreenBuffer (_screenBuffer))
{
throw new Win32Exception (Marshal.GetLastWin32Error ());
}
_originalStdOutChars = new CharInfo [size.Height * size.Width];
if (!ReadConsoleOutput (_screenBuffer, _originalStdOutChars, coords, new Coord { X = 0, Y = 0 }, ref window))
@@ -292,7 +431,7 @@ public partial class WindowsConsole
public bool SetCursorPosition (Coord position)
{
return SetConsoleCursorPosition (IsWindowsTerminal ? _outputHandle : _screenBuffer, position);
return SetConsoleCursorPosition (IsVirtualTerminal ? _outputHandle : _screenBuffer, position);
}
public void SetInitialCursorVisibility ()
@@ -305,14 +444,14 @@ public partial class WindowsConsole
public bool GetCursorVisibility (out CursorVisibility visibility)
{
if ((IsWindowsTerminal ? _outputHandle : _screenBuffer) == nint.Zero)
if ((IsVirtualTerminal ? _outputHandle : _screenBuffer) == nint.Zero)
{
visibility = CursorVisibility.Invisible;
return false;
}
if (!GetConsoleCursorInfo (IsWindowsTerminal ? _outputHandle : _screenBuffer, out ConsoleCursorInfo info))
if (!GetConsoleCursorInfo (IsVirtualTerminal ? _outputHandle : _screenBuffer, out ConsoleCursorInfo info))
{
int err = Marshal.GetLastWin32Error ();
@@ -380,7 +519,7 @@ public partial class WindowsConsole
bVisible = ((uint)visibility & 0xFF00) != 0
};
if (!SetConsoleCursorInfo (IsWindowsTerminal ? _outputHandle : _screenBuffer, ref info))
if (!SetConsoleCursorInfo (IsVirtualTerminal ? _outputHandle : _screenBuffer, ref info))
{
return false;
}
@@ -430,7 +569,7 @@ public partial class WindowsConsole
internal Size GetConsoleBufferWindow (out Point position)
{
if ((IsWindowsTerminal ? _outputHandle : _screenBuffer) == nint.Zero)
if ((IsVirtualTerminal ? _outputHandle : _screenBuffer) == nint.Zero)
{
position = Point.Empty;
@@ -440,7 +579,7 @@ public partial class WindowsConsole
var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX ();
csbi.cbSize = (uint)Marshal.SizeOf (csbi);
if (!GetConsoleScreenBufferInfoEx (IsWindowsTerminal ? _outputHandle : _screenBuffer, ref csbi))
if (!GetConsoleScreenBufferInfoEx (IsVirtualTerminal ? _outputHandle : _screenBuffer, ref csbi))
{
//throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
position = Point.Empty;
@@ -479,19 +618,19 @@ public partial class WindowsConsole
var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX ();
csbi.cbSize = (uint)Marshal.SizeOf (csbi);
if (!GetConsoleScreenBufferInfoEx (IsWindowsTerminal ? _outputHandle : _screenBuffer, ref csbi))
if (!GetConsoleScreenBufferInfoEx (IsVirtualTerminal ? _outputHandle : _screenBuffer, ref csbi))
{
throw new Win32Exception (Marshal.GetLastWin32Error ());
}
Coord maxWinSize = GetLargestConsoleWindowSize (IsWindowsTerminal ? _outputHandle : _screenBuffer);
Coord maxWinSize = GetLargestConsoleWindowSize (IsVirtualTerminal ? _outputHandle : _screenBuffer);
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 (IsWindowsTerminal ? _outputHandle : _screenBuffer, ref csbi))
if (!SetConsoleScreenBufferInfoEx (IsVirtualTerminal ? _outputHandle : _screenBuffer, ref csbi))
{
throw new Win32Exception (Marshal.GetLastWin32Error ());
}
@@ -509,11 +648,18 @@ public partial class WindowsConsole
return new (winRect.Right + 1, newRows - 1 < 0 ? 0 : winRect.Bottom + 1);
}
internal Size GetLargestConsoleWindowSize ()
{
Coord maxWinSize = GetLargestConsoleWindowSize (IsVirtualTerminal ? _outputHandle : _screenBuffer);
return new (maxWinSize.X, maxWinSize.Y);
}
private void SetConsoleOutputWindow (CONSOLE_SCREEN_BUFFER_INFOEX csbi)
{
if ((IsWindowsTerminal
if ((IsVirtualTerminal
? _outputHandle
: _screenBuffer) != nint.Zero && !SetConsoleScreenBufferInfoEx (IsWindowsTerminal ? _outputHandle : _screenBuffer, ref csbi))
: _screenBuffer) != nint.Zero && !SetConsoleScreenBufferInfoEx (IsVirtualTerminal ? _outputHandle : _screenBuffer, ref csbi))
{
throw new Win32Exception (Marshal.GetLastWin32Error ());
}
@@ -521,7 +667,7 @@ public partial class WindowsConsole
internal Size SetConsoleOutputWindow (out Point position)
{
if ((IsWindowsTerminal ? _outputHandle : _screenBuffer) == nint.Zero)
if ((IsVirtualTerminal ? _outputHandle : _screenBuffer) == nint.Zero)
{
position = Point.Empty;
@@ -531,7 +677,7 @@ public partial class WindowsConsole
var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX ();
csbi.cbSize = (uint)Marshal.SizeOf (csbi);
if (!GetConsoleScreenBufferInfoEx (IsWindowsTerminal ? _outputHandle : _screenBuffer, ref csbi))
if (!GetConsoleScreenBufferInfoEx (IsVirtualTerminal ? _outputHandle : _screenBuffer, ref csbi))
{
throw new Win32Exception (Marshal.GetLastWin32Error ());
}
@@ -556,7 +702,7 @@ public partial class WindowsConsole
return sz;
}
internal bool IsWindowsTerminal { get; set; }
internal bool IsVirtualTerminal { get; init; }
private uint ConsoleMode
{
@@ -573,6 +719,7 @@ public partial class WindowsConsole
public enum ConsoleModes : uint
{
EnableProcessedInput = 1,
EnableVirtualTerminalProcessing = 4,
EnableMouseInput = 16,
EnableQuickEditMode = 64,
EnableExtendedFlags = 128
@@ -791,11 +938,11 @@ public partial class WindowsConsole
public struct ExtendedCharInfo
{
public char Char { get; set; }
public char [] Char { get; set; }
public Attribute Attribute { get; set; }
public bool Empty { get; set; } // TODO: Temp hack until virtual terminal sequences
public ExtendedCharInfo (char character, Attribute attribute)
public ExtendedCharInfo (char [] character, Attribute attribute)
{
Char = character;
Attribute = attribute;
@@ -946,7 +1093,13 @@ public partial class WindowsConsole
);
[DllImport ("kernel32.dll", SetLastError = true)]
static extern bool FlushFileBuffers (nint hFile);
private static extern bool SetConsoleTextAttribute (
nint hConsoleOutput,
ushort wAttributes
);
[DllImport ("kernel32.dll", SetLastError = true)]
private static extern bool FlushFileBuffers (nint hFile);
[DllImport ("kernel32.dll")]
private static extern bool SetConsoleCursorPosition (nint hConsoleOutput, Coord dwCursorPosition);

View File

@@ -24,7 +24,7 @@ namespace Terminal.Gui.Drivers;
internal class WindowsDriver : ConsoleDriver
{
private readonly bool _isWindowsTerminal;
private readonly bool _isVirtualTerminal;
private WindowsConsole.SmallRect _damageRegion;
private bool _isButtonDoubleClicked;
@@ -57,18 +57,16 @@ internal class WindowsDriver : ConsoleDriver
// force 16color mode (.e.g ConEmu which really doesn't work well at all).
if (!RunningUnitTests)
{
WinConsole!.IsWindowsTerminal = _isWindowsTerminal =
Environment.GetEnvironmentVariable ("WT_SESSION") is { }
|| Environment.GetEnvironmentVariable ("VSAPPIDNAME") != null;
_isVirtualTerminal = WinConsole!.IsVirtualTerminal;
}
if (!_isWindowsTerminal)
if (!_isVirtualTerminal)
{
Force16Colors = true;
}
}
public override bool SupportsTrueColor => RunningUnitTests || (Environment.OSVersion.Version.Build >= 14931 && _isWindowsTerminal);
public override bool SupportsTrueColor => RunningUnitTests || (Environment.OSVersion.Version.Build >= 14931 && _isVirtualTerminal);
public WindowsConsole? WinConsole { get; private set; }
@@ -337,7 +335,7 @@ internal class WindowsDriver : ConsoleDriver
if (Contents [row, col].IsDirty == false)
{
_outputBuffer [position].Empty = true;
_outputBuffer [position].Char = (char)Rune.ReplacementChar.Value;
_outputBuffer [position].Char = [(char)Contents [row, col].Rune.Value];
continue;
}
@@ -346,12 +344,12 @@ internal class WindowsDriver : ConsoleDriver
if (Contents [row, col].Rune.IsBmp)
{
_outputBuffer [position].Char = (char)Contents [row, col].Rune.Value;
_outputBuffer [position].Char = [(char)Contents [row, col].Rune.Value];
}
else
{
//_outputBuffer [position].Empty = true;
_outputBuffer [position].Char = (char)Rune.ReplacementChar.Value;
_outputBuffer [position].Char = [(char)Contents [row, col].Rune.ToString () [0],
(char)Contents [row, col].Rune.ToString () [1]];
if (Contents [row, col].Rune.GetColumns () > 1 && col + 1 < Cols)
{
@@ -359,7 +357,7 @@ internal class WindowsDriver : ConsoleDriver
col++;
position = row * Cols + col;
_outputBuffer [position].Empty = false;
_outputBuffer [position].Char = ' ';
_outputBuffer [position].Char = ['\0'];
}
}
}
@@ -396,7 +394,7 @@ internal class WindowsDriver : ConsoleDriver
{
#if HACK_CHECK_WINCHANGED
_mainLoopDriver.WinChanged -= ChangeWin;
_mainLoopDriver.WinChanged -= ChangeWin!;
#endif
}
@@ -405,7 +403,7 @@ internal class WindowsDriver : ConsoleDriver
WinConsole?.Cleanup ();
WinConsole = null;
if (!RunningUnitTests && _isWindowsTerminal)
if (!RunningUnitTests && _isVirtualTerminal)
{
// Disable alternative screen buffer.
Console.Out.Write (EscSeqUtils.CSI_RestoreCursorAndRestoreAltBufferWithBackscroll);
@@ -422,9 +420,9 @@ internal class WindowsDriver : ConsoleDriver
{
if (WinConsole is { })
{
// BUGBUG: The results from GetConsoleOutputWindow are incorrect when called from Init.
// Our thread in WindowsMainLoop.CheckWin will get the correct results. See #if HACK_CHECK_WINCHANGED
Size winSize = WinConsole.GetConsoleOutputWindow (out _);
// The results from GetConsoleBufferWindow are correct when called from Init.
// Our thread in WindowsMainLoop.CheckWin will get the resize event. See #if HACK_CHECK_WINCHANGED
Size winSize = WinConsole.GetConsoleBufferWindow (out _);
Cols = winSize.Width;
Rows = winSize.Height;
OnSizeChanged (new SizeChangedEventArgs (new (Cols, Rows)));
@@ -432,7 +430,7 @@ internal class WindowsDriver : ConsoleDriver
WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion);
if (_isWindowsTerminal)
if (_isVirtualTerminal)
{
Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll);
}
@@ -463,7 +461,7 @@ internal class WindowsDriver : ConsoleDriver
ClearContents ();
#if HACK_CHECK_WINCHANGED
_mainLoopDriver.WinChanged = ChangeWin;
_mainLoopDriver.WinChanged = ChangeWin!;
#endif
if (!RunningUnitTests)
@@ -604,13 +602,6 @@ internal class WindowsDriver : ConsoleDriver
return;
}
int w = e.Size.Value.Width;
if (w == Cols - 3 && e.Size.Value.Height < Rows)
{
w += 3;
}
Left = 0;
Top = 0;
Cols = e.Size.Value.Width;
@@ -618,8 +609,8 @@ internal class WindowsDriver : ConsoleDriver
if (!RunningUnitTests)
{
Size newSize = WinConsole.SetConsoleWindow (
(short)Math.Max (w, 16),
Size newSize = WinConsole!.SetConsoleWindow (
(short)Math.Max (e.Size.Value.Width, 16),
(short)Math.Max (e.Size.Value.Height, 0));
Cols = newSize.Width;

View File

@@ -220,6 +220,7 @@ internal class WindowsMainLoop : IMainLoopDriver
private readonly ManualResetEventSlim _winChange = new (false);
private bool _winChanged;
private Size _windowSize;
private Size? _lastWindowSizeBeforeMaximized = null;
private void CheckWinChange ()
{
while (_mainLoop is { })
@@ -232,7 +233,22 @@ internal class WindowsMainLoop : IMainLoopDriver
while (_mainLoop is { })
{
Task.Delay (500).Wait ();
_windowSize = _winConsole.GetConsoleBufferWindow (out _);
Size largestWindowSize = _winConsole!.GetLargestConsoleWindowSize ();
_windowSize = _winConsole!.GetConsoleBufferWindow (out _);
if (_lastWindowSizeBeforeMaximized is null && _windowSize == largestWindowSize)
{
_lastWindowSizeBeforeMaximized = new (_consoleDriver.Cols, _consoleDriver.Rows);
}
else if (_lastWindowSizeBeforeMaximized is { } && _windowSize != largestWindowSize)
{
if (_windowSize != _lastWindowSizeBeforeMaximized)
{
_windowSize = _lastWindowSizeBeforeMaximized.Value;
}
_lastWindowSizeBeforeMaximized = null;
}
if (_windowSize != Size.Empty
&& (_windowSize.Width != _consoleDriver.Cols

View File

@@ -111,7 +111,7 @@ public class ComboBox : View, IDesignable
}
/// <inheritdoc />
protected override bool OnSettingScheme (ValueChangingEventArgs<Scheme?> args)
protected override bool OnSettingScheme (ValueChangingEventArgs<Scheme> args)
{
_listview.SetScheme(args.NewValue);
return base.OnSettingScheme (args);

View File

@@ -115,7 +115,7 @@ public class FileDialog : Dialog, IDesignable
_btnUp.Text = GetUpButtonText ();
_btnUp.Accepting += (s, e) =>
{
_history.Up ();
_history?.Up ();
e.Handled = true;
};
@@ -123,7 +123,7 @@ public class FileDialog : Dialog, IDesignable
_btnBack.Text = GetBackButtonText ();
_btnBack.Accepting += (s, e) =>
{
_history.Back ();
_history?.Back ();
e.Handled = true;
};
@@ -131,7 +131,7 @@ public class FileDialog : Dialog, IDesignable
_btnForward.Text = GetForwardButtonText ();
_btnForward.Accepting += (s, e) =>
{
_history.Forward ();
_history?.Forward ();
e.Handled = true;
};
@@ -605,7 +605,7 @@ public class FileDialog : Dialog, IDesignable
bool addCurrentStateToHistory,
bool setPathText = true,
bool clearForward = true,
string pathText = null
string? pathText = null
)
{
// no change of state
@@ -1109,7 +1109,7 @@ public class FileDialog : Dialog, IDesignable
bool addCurrentStateToHistory,
bool setPathText = true,
bool clearForward = true,
string pathText = null
string? pathText = null
)
{
if (State is SearchState search)

View File

@@ -264,28 +264,28 @@ public class NumericUpDown<T> : View where T : notnull
protected override bool OnDrawingText () { return true; }
/// <summary>
/// Attempts to convert the specified <paramref name="value"/> to type <typeparamref name="T"/>.
/// Attempts to convert the specified <paramref name="value"/> to type <typeparamref name="TValue"/>.
/// </summary>
/// <typeparam name="T">The type to which the value should be converted.</typeparam>
/// <typeparam name="TValue">The type to which the value should be converted.</typeparam>
/// <param name="value">The value to convert.</param>
/// <param name="result">
/// When this method returns, contains the converted value if the conversion succeeded,
/// or the default value of <typeparamref name="T"/> if the conversion failed.
/// or the default value of <typeparamref name="TValue"/> if the conversion failed.
/// </param>
/// <returns>
/// <c>true</c> if the conversion was successful; otherwise, <c>false</c>.
/// </returns>
public static bool TryConvert<T> (object value, out T? result)
public static bool TryConvert<TValue> (object value, out TValue? result)
{
try
{
result = (T)Convert.ChangeType (value, typeof (T));
result = (TValue)Convert.ChangeType (value, typeof (TValue));
return true;
}
catch
{
result = default (T);
result = default (TValue);
return false;
}

View File

@@ -1,6 +1,7 @@
using System.Collections.Concurrent;
using System.Drawing;
using TerminalGuiFluentTesting;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Terminal.Gui.Drivers;
@@ -15,26 +16,26 @@ public class FakeApplicationFactory
{
var cts = new CancellationTokenSource ();
var fakeInput = new FakeNetInput (cts.Token);
FakeOutput _output = new ();
_output.Size = new (25, 25);
FakeOutput output = new ();
output.Size = new (25, 25);
IApplication origApp = ApplicationImpl.Instance;
var sizeMonitor = new FakeSizeMonitor ();
var v2 = new ApplicationV2 (new FakeNetComponentFactory (fakeInput, _output, sizeMonitor));
var v2 = new ApplicationV2 (new FakeNetComponentFactory (fakeInput, output, sizeMonitor));
ApplicationImpl.ChangeInstance (v2);
v2.Init (null,"v2net");
var d = (ConsoleDriverFacade<ConsoleKeyInfo>)Application.Driver;
var d = (ConsoleDriverFacade<ConsoleKeyInfo>)Application.Driver!;
sizeMonitor.SizeChanging += (_, e) =>
{
if (e.Size != null)
{
var s = e.Size.Value;
_output.Size = s;
output.Size = s;
d.OutputBuffer.SetWindowSize (s.Width, s.Height);
}
};
@@ -95,7 +96,7 @@ class FakeDriverV2 : ConsoleDriverFacade<ConsoleKeyInfo>, IFakeDriverV2
{
public ConcurrentQueue<ConsoleKeyInfo> InputBuffer { get; }
public FakeSizeMonitor SizeMonitor { get; }
public OutputBuffer OutputBuffer { get; }
public new OutputBuffer OutputBuffer { get; }
public IConsoleOutput ConsoleOutput { get; }

View File

@@ -2,6 +2,7 @@
using System.Drawing;
using System.Text;
using Microsoft.Extensions.Logging;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace TerminalGuiFluentTesting;
@@ -386,7 +387,7 @@ public class GuiTestContext : IDisposable
{
case V2TestDriver.V2Win:
_winInput.InputBuffer.Enqueue (
_winInput.InputBuffer!.Enqueue (
new ()
{
EventType = WindowsConsole.EventType.Mouse,
@@ -410,7 +411,6 @@ public class GuiTestContext : IDisposable
return WaitUntil (() => _winInput.InputBuffer.IsEmpty);
break;
case V2TestDriver.V2Net:
int netButton = btn switch
@@ -471,8 +471,6 @@ public class GuiTestContext : IDisposable
}
return WaitIteration ();
;
}
/// <summary>
@@ -699,7 +697,7 @@ public class GuiTestContext : IDisposable
down.bKeyDown = true;
up.bKeyDown = false;
_winInput.InputBuffer.Enqueue (
_winInput.InputBuffer!.Enqueue (
new ()
{
EventType = WindowsConsole.EventType.Key,
@@ -718,7 +716,7 @@ public class GuiTestContext : IDisposable
private void SendNetKey (ConsoleKeyInfo consoleKeyInfo, bool wait = true)
{
_netInput.InputBuffer.Enqueue (consoleKeyInfo);
_netInput.InputBuffer!.Enqueue (consoleKeyInfo);
if (wait)
{
@@ -732,7 +730,7 @@ public class GuiTestContext : IDisposable
/// <param name="specialKey"></param>
private void SendWindowsKey (ConsoleKeyMapping.VK specialKey)
{
_winInput.InputBuffer.Enqueue (
_winInput.InputBuffer!.Enqueue (
new ()
{
EventType = WindowsConsole.EventType.Key,

View File

@@ -4,7 +4,7 @@ namespace TerminalGuiFluentTesting;
internal class TextWriterLogger (TextWriter writer) : ILogger
{
public IDisposable? BeginScope<TState> (TState state) { return null; }
public IDisposable? BeginScope<TState> (TState state) where TState : notnull { return null; }
public bool IsEnabled (LogLevel logLevel) { return true; }