Browse Source

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 <[email protected]>
BDisp 4 months ago
parent
commit
f98e460048

+ 5 - 1
Terminal.Gui/ConsoleDrivers/V2/ConsoleDriverFacade.cs

@@ -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"/>

+ 8 - 0
Terminal.Gui/ConsoleDrivers/V2/MainLoopCoordinator.cs

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

+ 21 - 5
Terminal.Gui/ConsoleDrivers/V2/WindowsOutput.cs

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

+ 124 - 120
Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsConsole.cs

@@ -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
-        //                                          );
-
-        //if (_screenBuffer == INVALID_HANDLE_VALUE)
-        //{
-        //    int err = Marshal.GetLastWin32Error ();
-
-        //    if (err != 0)
-        //    {
-        //        throw new Win32Exception (err);
-        //    }
-        //}
+        _screenBuffer = CreateConsoleScreenBuffer (
+                                                   DesiredAccess.GenericRead | DesiredAccess.GenericWrite,
+                                                   ShareMode.FileShareRead | ShareMode.FileShareWrite,
+                                                   nint.Zero,
+                                                   1,
+                                                   nint.Zero
+                                                  );
 
-        SetInitialCursorVisibility ();
+        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 (!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);
-
-    //    if (!GetConsoleScreenBufferInfoEx (_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);
-
-    //    if (!SetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi))
-    //    {
-    //        throw new Win32Exception (Marshal.GetLastWin32Error ());
-    //    }
-
-    //    var winRect = new SmallRect (0, 0, (short)(newCols - 1), (short)Math.Max (newRows - 1, 0));
-
-    //    if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect))
-    //    {
-    //        //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
-    //        return new (cols, rows);
-    //    }
-
-    //    SetConsoleOutputWindow (csbi);
-
-    //    return new (winRect.Right + 1, newRows - 1 < 0 ? 0 : winRect.Bottom + 1);
-    //}
-
-    //private void SetConsoleOutputWindow (CONSOLE_SCREEN_BUFFER_INFOEX csbi)
-    //{
-    //    if (_screenBuffer != nint.Zero && !SetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi))
-    //    {
-    //        throw new Win32Exception (Marshal.GetLastWin32Error ());
-    //    }
-    //}
-
-    //internal Size SetConsoleOutputWindow (out Point position)
-    //{
-    //    if (_screenBuffer == nint.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 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));
-
-    //    if (!SetConsoleScreenBufferInfoEx (_outputHandle, ref csbi))
-    //    {
-    //        throw new Win32Exception (Marshal.GetLastWin32Error ());
-    //    }
-
-    //    if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect))
-    //    {
-    //        throw new Win32Exception (Marshal.GetLastWin32Error ());
-    //    }
-
-    //    return sz;
-    //}
+    internal Size SetConsoleWindow (short cols, short rows)
+    {
+        var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX ();
+        csbi.cbSize = (uint)Marshal.SizeOf (csbi);
+
+        if (!GetConsoleScreenBufferInfoEx (IsWindowsTerminal ? _outputHandle : _screenBuffer, ref csbi))
+        {
+            throw new Win32Exception (Marshal.GetLastWin32Error ());
+        }
+
+        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 (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));
+
+        if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect))
+        {
+            //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
+            return new (cols, rows);
+        }
+
+        SetConsoleOutputWindow (csbi);
+
+        return new (winRect.Right + 1, newRows - 1 < 0 ? 0 : winRect.Bottom + 1);
+    }
+
+    private void SetConsoleOutputWindow (CONSOLE_SCREEN_BUFFER_INFOEX csbi)
+    {
+        if ((IsWindowsTerminal
+                ? _outputHandle
+                : _screenBuffer) != nint.Zero && !SetConsoleScreenBufferInfoEx (IsWindowsTerminal ? _outputHandle : _screenBuffer, ref csbi))
+        {
+            throw new Win32Exception (Marshal.GetLastWin32Error ());
+        }
+    }
+
+    internal Size SetConsoleOutputWindow (out Point position)
+    {
+        if ((IsWindowsTerminal ? _outputHandle : _screenBuffer) == nint.Zero)
+        {
+            position = Point.Empty;
+
+            return Size.Empty;
+        }
+
+        var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX ();
+        csbi.cbSize = (uint)Marshal.SizeOf (csbi);
+
+        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));
+
+        if (!SetConsoleScreenBufferInfoEx (_outputHandle, ref csbi))
+        {
+            throw new Win32Exception (Marshal.GetLastWin32Error ());
+        }
+
+        if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect))
+        {
+            throw new Win32Exception (Marshal.GetLastWin32Error ());
+        }
+
+        return sz;
+    }
+
+    internal bool IsWindowsTerminal { get; set; }
 
     private uint ConsoleMode
     {

+ 9 - 5
Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsDriver.cs

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

+ 2 - 0
Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsMainLoop.cs

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