Skip to content

Fixes #4139. Application.Run<T> isn't initializing properly by setting the Application.ForceDriver property #4142

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jun 12, 2025
Merged
7 changes: 7 additions & 0 deletions Examples/UICatalog/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@
"commandLineArgs": "dotnet UICatalog.dll --driver NetDriver",
"distributionName": ""
},
"WSL: UICatalog --driver v2net": {
"commandName": "Executable",
"executablePath": "wsl",
"commandLineArgs": "dotnet UICatalog.dll --driver v2net",
"distributionName": ""
},

"Benchmark All": {
"commandName": "Project",
"commandLineArgs": "--benchmark"
Expand Down
2 changes: 1 addition & 1 deletion Examples/UICatalog/UICatalog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ namespace UICatalog;
/// </remarks>
public class UICatalog
{
private static string _forceDriver = string.Empty;
private static string? _forceDriver = null;

public static string LogFilePath { get; set; } = string.Empty;
public static LoggingLevelSwitch LogLevelSwitch { get; } = new ();
Expand Down
31 changes: 18 additions & 13 deletions Terminal.Gui/App/Application.Initialization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,6 @@ public static partial class Application // Initialization (Init/Shutdown)
[RequiresDynamicCode ("AOT")]
public static void Init (IConsoleDriver? driver = null, string? driverName = null)
{
if (driverName?.StartsWith ("v2") ?? false)
{
ApplicationImpl.ChangeInstance (new ApplicationV2 ());
}

ApplicationImpl.Instance.Init (driver, driverName);
}

Expand Down Expand Up @@ -77,18 +72,14 @@ internal static void InternalInit (
throw new InvalidOperationException ("Init has already been called and must be bracketed by Shutdown.");
}

ForceDriver = string.IsNullOrWhiteSpace (driverName) ? ForceDriver : driverName;

if (!calledViaRunT)
{
// Reset all class variables (Application is a singleton).
ResetState (ignoreDisposed: true);
}

Debug.Assert (Navigation is null);
Navigation = new ();

Debug.Assert(Popover is null);
Popover = new ();

// For UnitTests
if (driver is { })
{
Expand All @@ -105,8 +96,6 @@ internal static void InternalInit (
}
}

AddKeyBindings ();

// Ignore Configuration for ForceDriver if driverName is specified
if (!string.IsNullOrEmpty (driverName))
{
Expand Down Expand Up @@ -137,6 +126,14 @@ internal static void InternalInit (
{
Driver = (IConsoleDriver)Activator.CreateInstance (driverType)!;
}
else if (ForceDriver?.StartsWith ("v2") ?? false)
{
ApplicationImpl.ChangeInstance (new ApplicationV2 ());
ApplicationImpl.Instance.Init (driver, ForceDriver);
Debug.Assert (Driver is { });

return;
}
else
{
throw new ArgumentException (
Expand All @@ -146,6 +143,14 @@ internal static void InternalInit (
}
}

Debug.Assert (Navigation is null);
Navigation = new ();

Debug.Assert (Popover is null);
Popover = new ();

AddKeyBindings ();

try
{
MainLoop = Driver!.Init ();
Expand Down
11 changes: 9 additions & 2 deletions Terminal.Gui/App/ApplicationImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public static void ChangeInstance (IApplication newApplication)
[RequiresDynamicCode ("AOT")]
public virtual void Init (IConsoleDriver? driver = null, string? driverName = null)
{
Application.InternalInit (driver, driverName);
Application.InternalInit (driver, string.IsNullOrWhiteSpace (driverName) ? Application.ForceDriver : driverName);
}

/// <summary>
Expand Down Expand Up @@ -85,7 +85,12 @@ public virtual T Run<T> (Func<Exception, bool>? errorHandler = null, IConsoleDri
if (!Application.Initialized)
{
// Init() has NOT been called.
Application.InternalInit (driver, null, true);
Application.InternalInit (driver, Application.ForceDriver, true);
}

if (Instance is ApplicationV2)
{
return Instance.Run<T> (errorHandler, driver);
}

var top = new T ();
Expand Down Expand Up @@ -227,6 +232,8 @@ public virtual void Shutdown ()

Application.OnInitializedChanged (this, new (in init));
}

_lazyInstance = new (() => new ApplicationImpl ());
}

/// <inheritdoc />
Expand Down
8 changes: 7 additions & 1 deletion Terminal.Gui/Drivers/NetDriver/NetEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ public NetEvents (IConsoleDriver consoleDriver)
{
_consoleDriver = consoleDriver ?? throw new ArgumentNullException (nameof (consoleDriver));

if (ConsoleDriver.RunningUnitTests)
{
return;
}

Task.Run (() =>
{
try
Expand All @@ -29,7 +34,8 @@ public NetEvents (IConsoleDriver consoleDriver)
{ }
}, _netEventsDisposed.Token);

Task.Run (() => {
Task.Run (() =>
{
try
{
CheckWindowSizeChange ();
Expand Down
14 changes: 13 additions & 1 deletion Terminal.Gui/Drivers/V2/ConsoleDriverFacade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,19 @@ private void CreateClipboard ()
}

/// <summary>Gets the location and size of the terminal screen.</summary>
public Rectangle Screen => new (new (0, 0), _output.GetWindowSize ());
public Rectangle Screen
{
get
{
if (ConsoleDriver.RunningUnitTests)
{
// In unit tests, we don't have a real output, so we return an empty rectangle.
return Rectangle.Empty;
}

return new (new (0, 0), _output.GetWindowSize ());
}
}

/// <summary>
/// Gets or sets the clip rectangle that <see cref="AddRune(Rune)"/> and <see cref="AddStr(string)"/> are subject
Expand Down
16 changes: 15 additions & 1 deletion Terminal.Gui/Drivers/V2/NetInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ public class NetInput : ConsoleInput<ConsoleKeyInfo>, INetInput
public NetInput ()
{
Logging.Logger.LogInformation ($"Creating {nameof (NetInput)}");

if (ConsoleDriver.RunningUnitTests)
{
return;
}

PlatformID p = Environment.OSVersion.Platform;

if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows)
Expand All @@ -39,7 +45,15 @@ public NetInput ()
}

/// <inheritdoc/>
protected override bool Peek () { return Console.KeyAvailable; }
protected override bool Peek ()
{
if (ConsoleDriver.RunningUnitTests)
{
return false;
}

return Console.KeyAvailable;
}

/// <inheritdoc/>
protected override IEnumerable<ConsoleKeyInfo> Read ()
Expand Down
16 changes: 15 additions & 1 deletion Terminal.Gui/Drivers/V2/NetOutput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ public void Write (ReadOnlySpan<char> text)
/// <inheritdoc/>
public void Write (IOutputBuffer buffer)
{
if (ConsoleDriver.RunningUnitTests)
{
return;
}

if (Console.WindowHeight < 1
|| buffer.Contents.Length != buffer.Rows * buffer.Cols
|| buffer.Rows != Console.WindowHeight)
Expand Down Expand Up @@ -197,7 +202,16 @@ public void Write (IOutputBuffer buffer)
}

/// <inheritdoc/>
public Size GetWindowSize () { return new (Console.WindowWidth, Console.WindowHeight); }
public Size GetWindowSize ()
{
if (ConsoleDriver.RunningUnitTests)
{
// For unit tests, we return a default size.
return Size.Empty;
}

return new (Console.WindowWidth, Console.WindowHeight);
}

private void WriteToConsole (StringBuilder output, ref int lastCol, int row, ref int outputWidth)
{
Expand Down
5 changes: 5 additions & 0 deletions Terminal.Gui/Drivers/V2/WindowSizeMonitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public WindowSizeMonitor (IConsoleOutput consoleOut, IOutputBuffer outputBuffer)
/// <inheritdoc/>
public bool Poll ()
{
if (ConsoleDriver.RunningUnitTests)
{
return false;
}

Size size = _consoleOut.GetWindowSize ();

if (size != _lastSize)
Expand Down
16 changes: 15 additions & 1 deletion Terminal.Gui/Drivers/V2/WindowsInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ out uint lpNumberOfEventsRead
public WindowsInput ()
{
Logging.Logger.LogInformation ($"Creating {nameof (WindowsInput)}");

if (ConsoleDriver.RunningUnitTests)
{
return;
}

_inputHandle = GetStdHandle (STD_INPUT_HANDLE);

GetConsoleMode (_inputHandle, out uint v);
Expand Down Expand Up @@ -110,5 +116,13 @@ protected override bool Peek ()
}
}

public override void Dispose () { SetConsoleMode (_inputHandle, _originalConsoleMode); }
public override void Dispose ()
{
if (ConsoleDriver.RunningUnitTests)
{
return;
}

SetConsoleMode (_inputHandle, _originalConsoleMode);
}
}
23 changes: 17 additions & 6 deletions Terminal.Gui/Drivers/V2/WindowsOutput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ public WindowsOutput ()
{
Logging.Logger.LogInformation ($"Creating {nameof (WindowsOutput)}");

if (ConsoleDriver.RunningUnitTests)
{
return;
}

_screenBuffer = CreateConsoleScreenBuffer (
DesiredAccess.GenericRead | DesiredAccess.GenericWrite,
ShareMode.FileShareRead | ShareMode.FileShareWrite,
Expand Down Expand Up @@ -171,12 +176,13 @@ public void Write (IOutputBuffer buffer)
};

//size, ExtendedCharInfo [] charInfoBuffer, Coord , SmallRect window,
if (!WriteToConsole (
new (buffer.Cols, buffer.Rows),
outputBuffer,
bufferCoords,
damageRegion,
Application.Driver!.Force16Colors))
if (!ConsoleDriver.RunningUnitTests
&& !WriteToConsole (
new (buffer.Cols, buffer.Rows),
outputBuffer,
bufferCoords,
damageRegion,
Application.Driver!.Force16Colors))
{
int err = Marshal.GetLastWin32Error ();

Expand Down Expand Up @@ -312,6 +318,11 @@ public Size GetWindowSize ()
/// <inheritdoc/>
public void SetCursorVisibility (CursorVisibility visibility)
{
if (ConsoleDriver.RunningUnitTests)
{
return;
}

if (Application.Driver!.Force16Colors)
{
var info = new WindowsConsole.ConsoleCursorInfo
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IO.Abstractions;
using System.Globalization;
using System.IO.Abstractions;
using System.IO.Abstractions.TestingHelpers;
using System.Runtime.InteropServices;
using TerminalGuiFluentTesting;
Expand All @@ -12,6 +13,7 @@ public class FileDialogFluentTests

public FileDialogFluentTests (ITestOutputHelper outputHelper)
{
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
_out = new TestOutputWriter (outputHelper);
}

Expand Down
2 changes: 2 additions & 0 deletions Tests/IntegrationTests/FluentTests/MenuBarv2Tests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Globalization;
using System.Reflection;
using TerminalGuiFluentTesting;
using Xunit.Abstractions;
Expand All @@ -13,6 +14,7 @@ public class MenuBarv2Tests

public MenuBarv2Tests (ITestOutputHelper outputHelper)
{
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
_out = new TestOutputWriter (outputHelper);
}

Expand Down
12 changes: 9 additions & 3 deletions Tests/IntegrationTests/FluentTests/PopverMenuTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Reflection;
using System.Globalization;
using TerminalGuiFluentTesting;
using Xunit.Abstractions;

Expand All @@ -7,9 +7,15 @@ namespace IntegrationTests.FluentTests;
/// <summary>
/// Tests for the PopoverMenu class
/// </summary>
public class PopoverMenuTests (ITestOutputHelper outputHelper)
public class PopoverMenuTests
{
private readonly TextWriter _out = new TestOutputWriter (outputHelper);
private readonly TextWriter _out;

public PopoverMenuTests (ITestOutputHelper outputHelper)
{
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
_out = new TestOutputWriter (outputHelper);
}

[Theory]
[ClassData (typeof (V2TestDrivers))]
Expand Down
Loading
Loading