Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e7d7af8
Fixes #4004. Driver "windows" broken in conhost and cmd
BDisp Nov 28, 2025
2b26eac
Merge branch 'v2_develop' into v2_4004_windows-driver-fix
BDisp Nov 28, 2025
ba2444b
Fix unit tests
BDisp Nov 28, 2025
c747509
Remove IsVirtualTerminal from IApplication. Add IDriverInternal and I…
BDisp Nov 28, 2025
34c862c
Fix result.IsSupported
BDisp Nov 29, 2025
cb11fbf
Remove internal interfaces and add them in the implementations classes
BDisp Nov 29, 2025
d820525
Move Sixel from IApplication to IDriver interface it's a characterist…
BDisp Nov 29, 2025
454f0f0
Only if IOutput is OutputBase then set the internal properties
BDisp Nov 29, 2025
fec6c96
Prevents driver windows error on Unix system
BDisp Nov 29, 2025
c72340e
Fix scenario sixel error
BDisp Nov 29, 2025
9115bad
Comment some tests because is keyboard layout dependent and shifted k…
BDisp Nov 29, 2025
7742679
Add 🇵🇹 regional indicators test proving they ca be joined as only one…
BDisp Nov 29, 2025
954d5e6
SetConsoleActiveScreenBuffer is already called by the constructor and…
BDisp Nov 29, 2025
654f9d5
Finally fixed non virtual terminal in windows driver
BDisp Nov 29, 2025
938a8c3
Add more Sixel unit tests
BDisp Nov 29, 2025
fbf56e0
Add unit tests for OutputBase class
BDisp Nov 29, 2025
2cd2336
Avoid emit escape sequence
BDisp Nov 30, 2025
ca3b4ac
Fix assertion failure in UICatalog
BDisp Nov 30, 2025
e926ddd
Let each driver to deal with the Sixel write
BDisp Nov 30, 2025
fc9f9b8
When Shutdown is called by the static Application then the Applicatio…
BDisp Nov 30, 2025
9144e0f
Add more OutputBase with Sixel unit tests
BDisp Nov 30, 2025
6a37e9f
Fix some issues with IsVirtualTerminal and Force16Colors with unit te…
BDisp Dec 1, 2025
c50c8b8
Add Sixel Detect method unit test
BDisp Dec 1, 2025
62ef64d
Make Sixel IsSupported and SupportsTransparency consistent with more …
BDisp Dec 2, 2025
a884cd2
Resolving merge conflicts
BDisp Dec 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Examples/UICatalog/Scenarios/Images.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ protected override void Dispose (bool disposing)
_sixelSupported.Dispose ();
_isDisposed = true;

Application.Sixel.Clear ();
Application.Driver?.Sixel.Clear ();
}

private void OpenImage (object sender, CommandEventArgs e)
Expand Down
12 changes: 8 additions & 4 deletions Examples/UICatalog/UICatalog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
{
private static string? _forceDriver;
private static string? _uiCatalogDriver;
private static string? _scenarioDriver;

Check warning on line 60 in Examples/UICatalog/UICatalog.cs

View workflow job for this annotation

GitHub Actions / Build All Configurations

The field 'UICatalog._scenarioDriver' is never used

Check warning on line 60 in Examples/UICatalog/UICatalog.cs

View workflow job for this annotation

GitHub Actions / Build All Configurations

The field 'UICatalog._scenarioDriver' is never used

public static string LogFilePath { get; set; } = string.Empty;
public static LoggingLevelSwitch LogLevelSwitch { get; } = new ();
Expand Down Expand Up @@ -433,8 +433,10 @@

// This call to Application.Shutdown brackets the Application.Init call
// made by Scenario.Init() above
// TODO: Throw if shutdown was not called already
Application.Shutdown ();
if (Application.Driver is { })
{
Application.Shutdown ();
}

VerifyObjectsWereDisposed ();

Expand Down Expand Up @@ -482,8 +484,10 @@

scenario.Dispose ();

// TODO: Throw if shutdown was not called already
Application.Shutdown ();
if (Application.Driver is { })
{
Application.Shutdown ();
}

return results;
}
Expand Down
6 changes: 3 additions & 3 deletions Terminal.Gui/App/Application.Driver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ public static string ForceDriver
/// <summary>Raised when <see cref="ForceDriver"/> changes.</summary>
public static event EventHandler<ValueChangedEventArgs<string>>? ForceDriverChanged;

/// <inheritdoc cref="IApplication.Sixel"/>
[Obsolete ("The legacy static Application object is going away.")]
public static List<SixelToRender> Sixel => ApplicationImpl.Instance.Sixel;
/// <inheritdoc cref="IDriver.Sixel"/>
[Obsolete ("The legacy static Application object is going away.")]
public static List<SixelToRender> Sixel => ApplicationImpl.Instance.Driver?.Sixel!;

/// <summary>Gets a list of <see cref="IDriver"/> types and type names that are available.</summary>
/// <returns></returns>
Expand Down
8 changes: 7 additions & 1 deletion Terminal.Gui/App/Application.Lifecycle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,13 @@ public static int? MainThreadId

/// <inheritdoc cref="IDisposable.Dispose"/>
[Obsolete ("The legacy static Application object is going away.")]
public static void Shutdown () => ApplicationImpl.Instance.Dispose ();
public static void Shutdown ()
{
ApplicationImpl.Instance.Dispose ();

// Use the static reset method to bypass the fence check
ApplicationImpl.ResetStateStatic ();
}

/// <inheritdoc cref="IApplication.Initialized"/>
[Obsolete ("The legacy static Application object is going away.")]
Expand Down
3 changes: 0 additions & 3 deletions Terminal.Gui/App/ApplicationImpl.Driver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ internal partial class ApplicationImpl
/// <inheritdoc/>
public string ForceDriver { get; set; } = string.Empty;

/// <inheritdoc/>
public List<SixelToRender> Sixel { get; } = new ();

/// <summary>
/// Creates the appropriate <see cref="IDriver"/> based on platform and driverName.
/// </summary>
Expand Down
9 changes: 3 additions & 6 deletions Terminal.Gui/App/ApplicationImpl.Lifecycle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -310,17 +310,14 @@ public void ResetState (bool ignoreDisposed = false)
Initialized = false;
MainThreadId = null;

// === 9. Clear graphics ===
Sixel.Clear ();

// === 10. Reset ForceDriver ===
// === 9. Reset ForceDriver ===
// Note: ForceDriver and Force16Colors are reset
// If they need to persist across Init/Shutdown cycles
// then the user of the library should manage that state
Force16Colors = false;
ForceDriver = string.Empty;

// === 11. Reset synchronization context ===
// === 10. Reset synchronization context ===
// IMPORTANT: Always reset sync context, even if not initialized
// This ensures cleanup works correctly even if Shutdown is called without Init
// Reset synchronization context to allow the user to run async/await,
Expand All @@ -329,7 +326,7 @@ public void ResetState (bool ignoreDisposed = false)
// (https://github.com/gui-cs/Terminal.Gui/issues/1084).
SynchronizationContext.SetSynchronizationContext (null);

// === 12. Unsubscribe from Application static property change events ===
// === 11. Unsubscribe from Application static property change events ===
UnsubscribeApplicationEvents ();
}

Expand Down
6 changes: 0 additions & 6 deletions Terminal.Gui/App/IApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -497,12 +497,6 @@ public IApplication Run<TRunnable> (Func<Exception, bool>? errorHandler = null,
/// </remarks>
bool ClearScreenNextIteration { get; set; }

/// <summary>
/// Collection of sixel images to write out to screen when updating.
/// Only add to this collection if you are sure terminal supports sixel format.
/// </summary>
List<SixelToRender> Sixel { get; }

#endregion Screen and Driver

#region Keyboard
Expand Down
6 changes: 3 additions & 3 deletions Terminal.Gui/Drawing/Sixel/SixelSupportDetector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public SixelSupportDetector (IDriver? driver) : this ()
public void Detect (Action<SixelSupportResult> resultCallback)
{
var result = new SixelSupportResult ();
result.SupportsTransparency = IsVirtualTerminal () || IsXtermWithTransparency ();
result.SupportsTransparency = IsVirtualTerminal () && IsXtermWithTransparency ();
IsSixelSupportedByDar (result, resultCallback);
}

Expand Down Expand Up @@ -155,9 +155,9 @@ private void QueueRequest (AnsiEscapeSequence req, Action<string> responseCallba

private static bool ResponseIndicatesSupport (string response) { return response.Split (';').Contains ("4"); }

private static bool IsVirtualTerminal ()
private bool IsVirtualTerminal ()
{
return !string.IsNullOrWhiteSpace (Environment.GetEnvironmentVariable ("WT_SESSION"));
return (_driver as DriverImpl)?.IsVirtualTerminal == true;
}

private static bool IsXtermWithTransparency ()
Expand Down
2 changes: 2 additions & 0 deletions Terminal.Gui/Drivers/DotNetDriver/NetOutput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public NetOutput ()
{
_isWinPlatform = true;
}

IsVirtualTerminal = true;
}

/// <inheritdoc/>
Expand Down
51 changes: 47 additions & 4 deletions Terminal.Gui/Drivers/DriverImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,31 @@ ISizeMonitor sizeMonitor
};

CreateClipboard ();

if (_output is OutputBase outputBase)
{
IsVirtualTerminal = outputBase.IsVirtualTerminal;
outputBase.Driver = this;
}
}

private bool _isVirtualTerminal = true;

/// <summary>
/// Gets or sets whether <see cref="IDriver"/> support for virtualized terminal sequences.
/// </summary>
internal bool IsVirtualTerminal
{
get => _isVirtualTerminal;
set
{
_isVirtualTerminal = value;

if (!_isVirtualTerminal)
{
Force16Colors = true;
}
}
}

/// <inheritdoc/>
Expand Down Expand Up @@ -199,16 +224,34 @@ public int Top

/// <inheritdoc/>

public bool SupportsTrueColor => true;
public bool SupportsTrueColor => _isVirtualTerminal;

/// <inheritdoc/>

public bool Force16Colors
{
get => Application.Force16Colors || !SupportsTrueColor;
set => Application.Force16Colors = value || !SupportsTrueColor;
get => Application.Force16Colors;
set
{
if (!_isVirtualTerminal && !Application.Force16Colors)
{
Application.Force16Colors = true;

return;
}

if (!_isVirtualTerminal && !value)
{
return;
}

Application.Force16Colors = value;
}
}

/// <inheritdoc/>
public List<SixelToRender> Sixel { get; } = [];

/// <inheritdoc/>

public Attribute CurrentAttribute
Expand Down Expand Up @@ -362,7 +405,7 @@ public Attribute SetAttribute (Attribute newAttribute)
public void EnqueueKeyEvent (Key key) { InputProcessor.EnqueueKeyDownEvent (key); }

/// <inheritdoc/>
public void QueueAnsiRequest (AnsiEscapeSequenceRequest request) { _ansiRequestScheduler.SendOrSchedule (this, request); }
public virtual void QueueAnsiRequest (AnsiEscapeSequenceRequest request) { _ansiRequestScheduler.SendOrSchedule (this, request); }

/// <inheritdoc/>
public AnsiRequestScheduler GetRequestScheduler () => _ansiRequestScheduler;
Expand Down
22 changes: 17 additions & 5 deletions Terminal.Gui/Drivers/FakeDriver/FakeOutput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public FakeOutput ()
{
LastBuffer = new OutputBufferImpl ();
LastBuffer.SetSize (80, 25);
IsVirtualTerminal = true;
}

/// <summary>
Expand Down Expand Up @@ -86,10 +87,21 @@ public void Dispose ()
/// <inheritdoc/>
protected override void AppendOrWriteAttribute (StringBuilder output, Attribute attr, TextStyle redrawTextStyle)
{
if (Application.Force16Colors)
if (Driver?.Force16Colors == true)
{
output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Foreground.GetAnsiColorCode ()));
output.Append (EscSeqUtils.CSI_SetBackgroundColor (attr.Background.GetAnsiColorCode ()));
if (IsVirtualTerminal)
{
output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Foreground.GetAnsiColorCode ()));
output.Append (EscSeqUtils.CSI_SetBackgroundColor (attr.Background.GetAnsiColorCode ()));

EscSeqUtils.CSI_AppendTextStyleChange (output, redrawTextStyle, attr.Style);
}
else
{
Write (output);
Console.ForegroundColor = (ConsoleColor)attr.Foreground.GetClosestNamedColor16 ();
Console.BackgroundColor = (ConsoleColor)attr.Background.GetClosestNamedColor16 ();
}
}
else
{
Expand All @@ -106,9 +118,9 @@ protected override void AppendOrWriteAttribute (StringBuilder output, Attribute
attr.Background.G,
attr.Background.B
);
}

EscSeqUtils.CSI_AppendTextStyleChange (output, redrawTextStyle, attr.Style);
EscSeqUtils.CSI_AppendTextStyleChange (output, redrawTextStyle, attr.Style);
}
}

/// <inheritdoc/>
Expand Down
6 changes: 6 additions & 0 deletions Terminal.Gui/Drivers/IDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ public interface IDriver
/// </remarks>
bool Force16Colors { get; set; }

/// <summary>
/// Collection of sixel images to write out to screen when updating.
/// Only add to this collection if you are sure terminal supports sixel format.
/// </summary>
List<SixelToRender> Sixel { get; }

/// <summary>
/// The <see cref="System.Attribute"/> that will be used for the next <see cref="AddRune(Rune)"/> or <see cref="AddStr"/>
/// call.
Expand Down
73 changes: 56 additions & 17 deletions Terminal.Gui/Drivers/OutputBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,30 @@ namespace Terminal.Gui.Drivers;
/// </summary>
public abstract class OutputBase
{
/// <summary>
/// Get or sets the <see cref="IDriver"/> instance associated with this output.
/// </summary>
internal IDriver? Driver { get; set; }

private bool _isVirtualTerminal;

/// <summary>
/// Gets or sets whether <see cref="IDriver"/> support for virtualized terminal sequences.
/// </summary>
internal bool IsVirtualTerminal
{
get => _isVirtualTerminal;
set
{
_isVirtualTerminal = value;

if (Driver is DriverImpl driverImpl)
{
driverImpl.IsVirtualTerminal = _isVirtualTerminal;
}
}
}

private CursorVisibility? _cachedCursorVisibility;

// Last text style used, for updating style with EscSeqUtils.CSI_AppendTextStyleChange().
Expand Down Expand Up @@ -82,24 +106,32 @@ public virtual void Write (IOutputBuffer buffer)

if (output.Length > 0)
{
SetCursorPositionImpl (lastCol, row);
if (IsVirtualTerminal)
{
SetCursorPositionImpl (lastCol, row);

// Wrap URLs with OSC 8 hyperlink sequences using the new Osc8UrlLinker
StringBuilder processed = Osc8UrlLinker.WrapOsc8 (output);
Write (processed);
// Wrap URLs with OSC 8 hyperlink sequences using the new Osc8UrlLinker
StringBuilder processed = Osc8UrlLinker.WrapOsc8 (output);
Write (processed);
}
else
{
Write (output);
}
}
}

// BUGBUG: The Sixel impl depends on the legacy static Application object
// BUGBUG: Disabled for now
//foreach (SixelToRender s in Application.Sixel)
//{
// if (!string.IsNullOrWhiteSpace (s.SixelData))
// {
// SetCursorPositionImpl (s.ScreenPosition.X, s.ScreenPosition.Y);
// Console.Out.Write (s.SixelData);
// }
//}
if (Driver is { } && IsVirtualTerminal)
{
foreach (SixelToRender s in Driver?.Sixel!)
{
if (!string.IsNullOrWhiteSpace (s.SixelData))
{
SetCursorPositionImpl (s.ScreenPosition.X, s.ScreenPosition.Y);
Write ((StringBuilder)new (s.SixelData));
}
}
}

SetCursorVisibility (savedVisibility ?? CursorVisibility.Default);
_cachedCursorVisibility = savedVisibility;
Expand Down Expand Up @@ -230,9 +262,16 @@ private void WriteToConsole (StringBuilder output, ref int lastCol, int row, ref
{
SetCursorPositionImpl (lastCol, row);

// Wrap URLs with OSC 8 hyperlink sequences using the new Osc8UrlLinker
StringBuilder processed = Osc8UrlLinker.WrapOsc8 (output);
Write (processed);
if (IsVirtualTerminal)
{
// Wrap URLs with OSC 8 hyperlink sequences using the new Osc8UrlLinker
StringBuilder processed = Osc8UrlLinker.WrapOsc8 (output);
Write (processed);
}
else
{
Write (output);
}

output.Clear ();
lastCol += outputWidth;
Expand Down
7 changes: 6 additions & 1 deletion Terminal.Gui/Drivers/UnixDriver/UnixOutput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,15 @@ private struct WinSize
[DllImport ("libc", SetLastError = true)]
private static extern int dup (int fd);

public UnixOutput ()
{
IsVirtualTerminal = true;
}

/// <inheritdoc />
protected override void AppendOrWriteAttribute (StringBuilder output, Attribute attr, TextStyle redrawTextStyle)
{
if (Application.Force16Colors)
if (Driver?.Force16Colors == true)
{
output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Foreground.GetAnsiColorCode ()));
output.Append (EscSeqUtils.CSI_SetBackgroundColor (attr.Background.GetAnsiColorCode ()));
Expand Down
Loading
Loading