Fixes #4053. v2 WindowsDriver and v2win doesn't show any scenario in the UICatalog with cmd or conhost (#4055)

* Fix WindowsDriver to work with non-WindowsTerminal

* Fix unit test failure

* Fix v2win to work with non-WindowsTerminal

* Force16Colors isn't being setting in v2win driver on changing.

---------

Co-authored-by: Tig <tig@users.noreply.github.com>
This commit is contained in:
BDisp
2025-05-09 15:53:02 +01:00
committed by GitHub
parent ece4fee8f8
commit f98e460048
6 changed files with 152 additions and 114 deletions

View File

@@ -141,7 +141,11 @@ internal class ConsoleDriverFacade<T> : IConsoleDriver, IConsoleDriverFacade
/// <see langword="false"/>, indicating that the <see cref="ConsoleDriver"/> cannot support TrueColor.
/// </para>
/// </remarks>
public bool Force16Colors { get; set; }
public bool Force16Colors
{
get => Application.Force16Colors || !SupportsTrueColor;
set => Application.Force16Colors = value || !SupportsTrueColor;
}
/// <summary>
/// The <see cref="Attribute"/> that will be used for the next <see cref="AddRune(Rune)"/> or <see cref="AddStr"/>

View File

@@ -25,6 +25,7 @@ internal class MainLoopCoordinator<T> : IMainLoopCoordinator
private ConsoleDriverFacade<T> _facade;
private Task _inputTask;
private readonly ITimedEvents _timedEvents;
private readonly bool _isWindowsTerminal;
private readonly SemaphoreSlim _startupSemaphore = new (0, 1);
@@ -60,6 +61,7 @@ internal class MainLoopCoordinator<T> : IMainLoopCoordinator
_inputProcessor = inputProcessor;
_outputFactory = outputFactory;
_loop = loop;
_isWindowsTerminal = Environment.GetEnvironmentVariable ("WT_SESSION") is { } || Environment.GetEnvironmentVariable ("VSAPPIDNAME") != null;
}
/// <summary>
@@ -159,6 +161,12 @@ internal class MainLoopCoordinator<T> : IMainLoopCoordinator
_output,
_loop.AnsiRequestScheduler,
_loop.WindowSizeMonitor);
if (!_isWindowsTerminal)
{
Application.Force16Colors = _facade.Force16Colors = true;
}
Application.Driver = _facade;
_startupSemaphore.Release ();

View File

@@ -56,6 +56,9 @@ internal partial class WindowsOutput : IConsoleOutput
[DllImport ("kernel32.dll")]
private static extern bool SetConsoleCursorPosition (nint hConsoleOutput, Coord dwCursorPosition);
[DllImport ("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCursorInfo (nint hConsoleOutput, [In] ref ConsoleCursorInfo lpConsoleCursorInfo);
private readonly nint _screenBuffer;
public WindowsOutput ()
@@ -170,7 +173,7 @@ internal partial class WindowsOutput : IConsoleOutput
outputBuffer,
bufferCoords,
damageRegion,
false))
Application.Driver!.Force16Colors))
{
int err = Marshal.GetLastWin32Error ();
@@ -304,10 +307,23 @@ internal partial class WindowsOutput : IConsoleOutput
/// <inheritdoc/>
public void SetCursorVisibility (CursorVisibility visibility)
{
string cursorVisibilitySequence = visibility != CursorVisibility.Invisible
? EscSeqUtils.CSI_ShowCursor
: EscSeqUtils.CSI_HideCursor;
Write (cursorVisibilitySequence);
if (Application.Driver!.Force16Colors)
{
var info = new ConsoleCursorInfo
{
dwSize = (uint)visibility & 0x00FF,
bVisible = ((uint)visibility & 0xFF00) != 0
};
SetConsoleCursorInfo (_screenBuffer, ref info);
}
else
{
string cursorVisibilitySequence = visibility != CursorVisibility.Invisible
? EscSeqUtils.CSI_ShowCursor
: EscSeqUtils.CSI_HideCursor;
Write (cursorVisibilitySequence);
}
}
private Point _lastCursorPosition;

View File

@@ -17,7 +17,7 @@ internal partial class WindowsConsole
private readonly nint _inputHandle;
private nint _outputHandle;
//private nint _screenBuffer;
private nint _screenBuffer;
private readonly uint _originalConsoleMode;
private CursorVisibility? _initialCursorVisibility;
private CursorVisibility? _currentCursorVisibility;
@@ -147,10 +147,12 @@ internal partial class WindowsConsole
{
//Debug.WriteLine ("WriteToConsole");
//if (_screenBuffer == nint.Zero)
//{
// ReadFromConsoleOutput (size, bufferSize, ref window);
//}
if (!IsWindowsTerminal && _screenBuffer == nint.Zero)
{
ReadFromConsoleOutput (size, bufferSize, ref window);
}
SetInitialCursorVisibility ();
var result = false;
@@ -169,7 +171,7 @@ internal partial class WindowsConsole
};
}
result = WriteConsoleOutput (_outputHandle, ci, bufferSize, new Coord { X = window.Left, Y = window.Top }, ref window);
result = WriteConsoleOutput (IsWindowsTerminal ? _outputHandle : _screenBuffer, ci, bufferSize, new Coord { X = window.Left, Y = window.Top }, ref window);
}
else
{
@@ -222,7 +224,7 @@ internal partial class WindowsConsole
foreach (var sixel in Application.Sixel)
{
SetCursorPosition (new Coord ((short)sixel.ScreenPosition.X, (short)sixel.ScreenPosition.Y));
WriteConsole (_outputHandle, sixel.SixelData, (uint)sixel.SixelData.Length, out uint _, nint.Zero);
WriteConsole (IsWindowsTerminal ? _outputHandle : _screenBuffer, sixel.SixelData, (uint)sixel.SixelData.Length, out uint _, nint.Zero);
}
}
@@ -252,34 +254,32 @@ internal 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
// );
_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 (_screenBuffer == INVALID_HANDLE_VALUE)
{
int err = Marshal.GetLastWin32Error ();
// if (err != 0)
// {
// throw new Win32Exception (err);
// }
//}
if (err != 0)
{
throw new Win32Exception (err);
}
}
SetInitialCursorVisibility ();
//if (!SetConsoleActiveScreenBuffer (_screenBuffer))
//{
// throw new Win32Exception (Marshal.GetLastWin32Error ());
//}
if (!SetConsoleActiveScreenBuffer (_screenBuffer))
{
throw new Win32Exception (Marshal.GetLastWin32Error ());
}
_originalStdOutChars = new CharInfo [size.Height * size.Width];
if (!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 Win32Exception (Marshal.GetLastWin32Error ());
}
@@ -287,7 +287,7 @@ internal partial class WindowsConsole
public bool SetCursorPosition (Coord position)
{
return SetConsoleCursorPosition (_outputHandle, position);
return SetConsoleCursorPosition (IsWindowsTerminal ? _outputHandle : _screenBuffer, position);
}
public void SetInitialCursorVisibility ()
@@ -300,14 +300,14 @@ internal partial class WindowsConsole
public bool GetCursorVisibility (out CursorVisibility visibility)
{
if (_outputHandle == nint.Zero)
if ((IsWindowsTerminal ? _outputHandle : _screenBuffer) == nint.Zero)
{
visibility = CursorVisibility.Invisible;
return false;
}
if (!GetConsoleCursorInfo (_outputHandle, out ConsoleCursorInfo info))
if (!GetConsoleCursorInfo (IsWindowsTerminal ? _outputHandle : _screenBuffer, out ConsoleCursorInfo info))
{
int err = Marshal.GetLastWin32Error ();
@@ -375,7 +375,7 @@ internal partial class WindowsConsole
bVisible = ((uint)visibility & 0xFF00) != 0
};
if (!SetConsoleCursorInfo (_outputHandle, ref info))
if (!SetConsoleCursorInfo (IsWindowsTerminal ? _outputHandle : _screenBuffer, ref info))
{
return false;
}
@@ -411,12 +411,12 @@ internal partial class WindowsConsole
Console.WriteLine ("Error: {0}", err);
}
//if (_screenBuffer != nint.Zero)
//{
// CloseHandle (_screenBuffer);
//}
if (_screenBuffer != nint.Zero)
{
CloseHandle (_screenBuffer);
}
//_screenBuffer = nint.Zero;
_screenBuffer = nint.Zero;
_inputReadyCancellationTokenSource?.Cancel ();
_inputReadyCancellationTokenSource?.Dispose ();
@@ -425,7 +425,7 @@ internal partial class WindowsConsole
internal Size GetConsoleBufferWindow (out Point position)
{
if (_outputHandle == nint.Zero)
if ((IsWindowsTerminal ? _outputHandle : _screenBuffer) == nint.Zero)
{
position = Point.Empty;
@@ -435,7 +435,7 @@ internal partial class WindowsConsole
var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX ();
csbi.cbSize = (uint)Marshal.SizeOf (csbi);
if (!GetConsoleScreenBufferInfoEx (_outputHandle, ref csbi))
if (!GetConsoleScreenBufferInfoEx (IsWindowsTerminal ? _outputHandle : _screenBuffer, ref csbi))
{
//throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
position = Point.Empty;
@@ -469,85 +469,89 @@ internal partial class WindowsConsole
return sz;
}
//internal Size SetConsoleWindow (short cols, short rows)
//{
// var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX ();
// csbi.cbSize = (uint)Marshal.SizeOf (csbi);
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 Win32Exception (Marshal.GetLastWin32Error ());
// }
if (!GetConsoleScreenBufferInfoEx (IsWindowsTerminal ? _outputHandle : _screenBuffer, ref csbi))
{
throw new Win32Exception (Marshal.GetLastWin32Error ());
}
// Coord maxWinSize = GetLargestConsoleWindowSize (_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);
Coord maxWinSize = GetLargestConsoleWindowSize (IsWindowsTerminal ? _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 (_screenBuffer, ref csbi))
// {
// throw new Win32Exception (Marshal.GetLastWin32Error ());
// }
if (!SetConsoleScreenBufferInfoEx (IsWindowsTerminal ? _outputHandle : _screenBuffer, ref csbi))
{
throw new Win32Exception (Marshal.GetLastWin32Error ());
}
// var winRect = new SmallRect (0, 0, (short)(newCols - 1), (short)Math.Max (newRows - 1, 0));
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);
// }
if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect))
{
//throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
return new (cols, rows);
}
// SetConsoleOutputWindow (csbi);
SetConsoleOutputWindow (csbi);
// return new (winRect.Right + 1, newRows - 1 < 0 ? 0 : winRect.Bottom + 1);
//}
return new (winRect.Right + 1, newRows - 1 < 0 ? 0 : winRect.Bottom + 1);
}
//private void SetConsoleOutputWindow (CONSOLE_SCREEN_BUFFER_INFOEX csbi)
//{
// if (_screenBuffer != nint.Zero && !SetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi))
// {
// throw new Win32Exception (Marshal.GetLastWin32Error ());
// }
//}
private void SetConsoleOutputWindow (CONSOLE_SCREEN_BUFFER_INFOEX csbi)
{
if ((IsWindowsTerminal
? _outputHandle
: _screenBuffer) != nint.Zero && !SetConsoleScreenBufferInfoEx (IsWindowsTerminal ? _outputHandle : _screenBuffer, ref csbi))
{
throw new Win32Exception (Marshal.GetLastWin32Error ());
}
}
//internal Size SetConsoleOutputWindow (out Point position)
//{
// if (_screenBuffer == nint.Zero)
// {
// position = Point.Empty;
internal Size SetConsoleOutputWindow (out Point position)
{
if ((IsWindowsTerminal ? _outputHandle : _screenBuffer) == nint.Zero)
{
position = Point.Empty;
// return Size.Empty;
// }
return Size.Empty;
}
// var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX ();
// csbi.cbSize = (uint)Marshal.SizeOf (csbi);
var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX ();
csbi.cbSize = (uint)Marshal.SizeOf (csbi);
// if (!GetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi))
// {
// throw new Win32Exception (Marshal.GetLastWin32Error ());
// }
if (!GetConsoleScreenBufferInfoEx (IsWindowsTerminal ? _outputHandle : _screenBuffer, ref csbi))
{
throw new Win32Exception (Marshal.GetLastWin32Error ());
}
// Size sz = new (
// csbi.srWindow.Right - csbi.srWindow.Left + 1,
// Math.Max (csbi.srWindow.Bottom - csbi.srWindow.Top + 1, 0));
// position = new (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));
Size sz = new (
csbi.srWindow.Right - csbi.srWindow.Left + 1,
Math.Max (csbi.srWindow.Bottom - csbi.srWindow.Top + 1, 0));
position = new (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 (!SetConsoleScreenBufferInfoEx (_outputHandle, ref csbi))
// {
// throw new Win32Exception (Marshal.GetLastWin32Error ());
// }
if (!SetConsoleScreenBufferInfoEx (_outputHandle, ref csbi))
{
throw new Win32Exception (Marshal.GetLastWin32Error ());
}
// if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect))
// {
// throw new Win32Exception (Marshal.GetLastWin32Error ());
// }
if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect))
{
throw new Win32Exception (Marshal.GetLastWin32Error ());
}
// return sz;
//}
return sz;
}
internal bool IsWindowsTerminal { get; set; }
private uint ConsoleMode
{

View File

@@ -14,7 +14,7 @@
// the WindowsConsole.EventType.WindowBufferSize event. However, on Init the window size is
// still incorrect so we still need this hack.
//#define HACK_CHECK_WINCHANGED
#define HACK_CHECK_WINCHANGED
using System.ComponentModel;
using System.Diagnostics;
@@ -57,8 +57,12 @@ internal class WindowsDriver : ConsoleDriver
// TODO: if some other Windows-based terminal supports true color, update this logic to not
// force 16color mode (.e.g ConEmu which really doesn't work well at all).
_isWindowsTerminal = _isWindowsTerminal =
Environment.GetEnvironmentVariable ("WT_SESSION") is { } || Environment.GetEnvironmentVariable ("VSAPPIDNAME") != null;
if (!RunningUnitTests)
{
WinConsole!.IsWindowsTerminal = _isWindowsTerminal =
Environment.GetEnvironmentVariable ("WT_SESSION") is { }
|| Environment.GetEnvironmentVariable ("VSAPPIDNAME") != null;
}
if (!_isWindowsTerminal)
{
@@ -422,7 +426,7 @@ internal class WindowsDriver : ConsoleDriver
{
// 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 Point _);
Size winSize = WinConsole.GetConsoleOutputWindow (out _);
Cols = winSize.Width;
Rows = winSize.Height;
OnSizeChanged (new SizeChangedEventArgs (new (Cols, Rows)));
@@ -466,7 +470,7 @@ internal class WindowsDriver : ConsoleDriver
if (!RunningUnitTests)
{
WinConsole?.SetInitialCursorVisibility ();
WinConsole?.SetInitialCursorVisibility ();
}
return new MainLoop (_mainLoopDriver);

View File

@@ -1,5 +1,7 @@
#nullable enable
#define HACK_CHECK_WINCHANGED
using System.Collections.Concurrent;
namespace Terminal.Gui;