Replace IsWindowsTerminal with IsVirtualTerminal

This commit is contained in:
BDisp
2025-07-22 16:24:12 +01:00
parent fea1d4f5c0
commit 7097c0b7e3
7 changed files with 118 additions and 51 deletions

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

@@ -28,6 +28,15 @@ internal class ConsoleDriverFacade<T> : IConsoleDriver, IConsoleDriverFacade
_outputBuffer = outputBuffer;
_ansiRequestScheduler = ansiRequestScheduler;
if (InputProcessor is WindowsInputProcessor)
{
SupportsTrueColor = new WindowsInput ().IsVirtualTerminal ();
}
else if (InputProcessor is NetInputProcessor)
{
SupportsTrueColor = Application.Driver.SupportsTrueColor;
}
InputProcessor.KeyDown += (s, e) => KeyDown?.Invoke (s, e);
InputProcessor.KeyUp += (s, e) => KeyUp?.Invoke (s, e);
InputProcessor.MouseEvent += (s, e) =>
@@ -145,7 +154,7 @@ internal class ConsoleDriverFacade<T> : IConsoleDriver, IConsoleDriverFacade
// TODO: Probably not everyone right?
/// <summary>Gets whether the <see cref="ConsoleDriver"/> supports TrueColor output.</summary>
public bool SupportsTrueColor => true;
public bool SupportsTrueColor { get; init; } = true;
// TODO: Currently ignored
/// <summary>

View File

@@ -25,7 +25,6 @@ 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);
@@ -61,7 +60,6 @@ internal class MainLoopCoordinator<T> : IMainLoopCoordinator
_inputProcessor = inputProcessor;
_outputFactory = outputFactory;
_loop = loop;
_isWindowsTerminal = Environment.GetEnvironmentVariable ("WT_SESSION") is { } || Environment.GetEnvironmentVariable ("VSAPPIDNAME") != null;
}
/// <summary>
@@ -162,7 +160,15 @@ internal class MainLoopCoordinator<T> : IMainLoopCoordinator
_loop.AnsiRequestScheduler,
_loop.WindowSizeMonitor);
if (!_isWindowsTerminal)
if (_facade.SupportsTrueColor)
{
if (!ConsoleDriver.RunningUnitTests)
{
// Enable alternative screen buffer.
Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll);
}
}
else
{
Application.Force16Colors = _facade.Force16Colors = true;
}
@@ -187,6 +193,13 @@ internal class MainLoopCoordinator<T> : IMainLoopCoordinator
_stopCalled = true;
_tokenSource.Cancel ();
if (!ConsoleDriver.RunningUnitTests && _facade.SupportsTrueColor)
{
// Disable alternative screen buffer.
Console.Out.Write (EscSeqUtils.CSI_RestoreCursorAndRestoreAltBufferWithBackscroll);
}
_output.Dispose ();
// Wait for input infinite loop to exit

View File

@@ -56,6 +56,12 @@ internal class WindowsInput : ConsoleInput<WindowsConsole.InputRecord>, IWindows
SetConsoleMode (_inputHandle, newConsoleMode);
}
internal bool IsVirtualTerminal ()
{
nint outputHandle = GetStdHandle (STD_OUTPUT_HANDLE);
return GetConsoleMode (outputHandle, out uint mode) && (mode & (uint)ConsoleModes.EnableVirtualTerminalProcessing) != 0;
}
protected override bool Peek ()
{
const int bufferSize = 1; // We only need to check if there's at least one event

View File

@@ -59,7 +59,17 @@ internal partial class WindowsOutput : IConsoleOutput
[DllImport ("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCursorInfo (nint hConsoleOutput, [In] ref WindowsConsole.ConsoleCursorInfo lpConsoleCursorInfo);
private readonly nint _screenBuffer;
[DllImport ("kernel32.dll", SetLastError = true)]
private static extern nint GetStdHandle (int nStdHandle);
[DllImport ("kernel32.dll")]
private static extern bool GetConsoleMode (nint hConsoleHandle, out uint lpMode);
[DllImport ("kernel32.dll")]
private static extern bool SetConsoleMode (nint hConsoleHandle, uint dwMode);
private nint _screenBuffer;
private nint _outputHandle;
// Last text style used, for updating style with EscSeqUtils.CSI_AppendTextStyleChange().
private TextStyle _redrawTextStyle = TextStyle.None;
@@ -73,33 +83,61 @@ internal partial class WindowsOutput : IConsoleOutput
return;
}
_screenBuffer = CreateConsoleScreenBuffer (
DesiredAccess.GenericRead | DesiredAccess.GenericWrite,
ShareMode.FileShareRead | ShareMode.FileShareWrite,
nint.Zero,
1,
nint.Zero
);
_outputHandle = GetStdHandle (STD_OUTPUT_HANDLE);
if (_screenBuffer == INVALID_HANDLE_VALUE)
if (!GetConsoleMode (_outputHandle, out uint mode))
{
int err = Marshal.GetLastWin32Error ();
if (err != 0)
{
throw new Win32Exception (err);
}
throw new ApplicationException ($"Failed to get _outputHandle console mode, error code: {Marshal.GetLastWin32Error ()}.");
}
if (!SetConsoleActiveScreenBuffer (_screenBuffer))
IsVirtualTerminal = (mode & (uint)ConsoleModes.EnableVirtualTerminalProcessing) != 0;
if (!IsVirtualTerminal)
{
throw new Win32Exception (Marshal.GetLastWin32Error ());
_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 ());
}
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 ()}.");
}
}
}
internal bool IsVirtualTerminal { get; init; }
public void Write (ReadOnlySpan<char> str)
{
if (!WriteConsole (_screenBuffer, str, (uint)str.Length, out uint _, nint.Zero))
if (!WriteConsole (IsVirtualTerminal ? _outputHandle : _screenBuffer, str, (uint)str.Length, out uint _, nint.Zero))
{
throw new Win32Exception (Marshal.GetLastWin32Error (), "Failed to write to console screen buffer.");
}
@@ -270,7 +308,7 @@ internal partial class WindowsOutput : IConsoleOutput
stringBuilder.CopyTo (0, writeBuffer, stringBuilder.Length);
// Supply console with the new content.
result = WriteConsole (_screenBuffer, writeBuffer, (uint)writeBuffer.Length, out uint _, nint.Zero);
result = WriteConsole (IsVirtualTerminal ? _outputHandle : _screenBuffer, writeBuffer, (uint)writeBuffer.Length, out uint _, nint.Zero);
}
finally
{
@@ -280,7 +318,7 @@ internal partial class WindowsOutput : IConsoleOutput
foreach (SixelToRender sixel in Application.Sixel)
{
SetCursorPosition ((short)sixel.ScreenPosition.X, (short)sixel.ScreenPosition.Y);
WriteConsole (_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);
}
}

View File

@@ -35,7 +35,9 @@ internal partial class WindowsConsole
newConsoleMode &= ~(uint)ConsoleModes.EnableProcessedInput;
ConsoleMode = newConsoleMode;
_inputReadyCancellationTokenSource = new ();
IsVirtualTerminal = GetConsoleMode (_outputHandle, out uint mode) && (mode & (uint)ConsoleModes.EnableVirtualTerminalProcessing) != 0;
_inputReadyCancellationTokenSource = new ();
Task.Run (ProcessInputQueue, _inputReadyCancellationTokenSource.Token);
}
@@ -150,7 +152,7 @@ internal partial class WindowsConsole
{
//Debug.WriteLine ("WriteToConsole");
if (!IsWindowsTerminal && _screenBuffer == nint.Zero)
if (!IsVirtualTerminal && _screenBuffer == nint.Zero)
{
ReadFromConsoleOutput (size, bufferSize, ref window);
}
@@ -229,7 +231,7 @@ internal 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);
}
}
@@ -292,7 +294,7 @@ internal 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 +307,14 @@ internal 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 +382,7 @@ internal 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 +432,7 @@ internal 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 +442,7 @@ internal 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 +481,19 @@ internal 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 ());
}
@@ -511,9 +513,9 @@ internal partial class WindowsConsole
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 +523,7 @@ internal 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 +533,7 @@ internal 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 +558,7 @@ internal partial class WindowsConsole
return sz;
}
internal bool IsWindowsTerminal { get; set; }
internal bool IsVirtualTerminal { get; init; }
private uint ConsoleMode
{
@@ -573,6 +575,7 @@ internal partial class WindowsConsole
public enum ConsoleModes : uint
{
EnableProcessedInput = 1,
EnableVirtualTerminalProcessing = 4,
EnableMouseInput = 16,
EnableQuickEditMode = 64,
EnableExtendedFlags = 128

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; }
@@ -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);
@@ -432,7 +430,7 @@ internal class WindowsDriver : ConsoleDriver
WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion);
if (_isWindowsTerminal)
if (_isVirtualTerminal)
{
Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll);
}