Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
e7f4169
Merge branch 'v2_develop' of tig:tig/Terminal.Gui into v2_develop
tig Mar 5, 2026
d4bdf24
Merge branch 'v2_develop' of tig:tig/Terminal.Gui into v2_develop
tig Mar 7, 2026
9f8d51e
Merge branch 'v2_develop' of tig:tig/Terminal.Gui into v2_develop
tig Mar 7, 2026
945094b
Update kitty keyboard support plan
tig Mar 7, 2026
32acf8c
Merge branch 'v2_develop' into feature/kitty
tig Mar 7, 2026
e691925
Merge branch 'feature/kitty' of tig:tig/Terminal.Gui into feature/kitty
tig Mar 7, 2026
c1ae836
Add kitty keyboard protocol phase 1 support
tig Mar 7, 2026
e7e35a7
Update kitty phase 1 progress docs
tig Mar 8, 2026
3b3fb12
Finish kitty keyboard protocol phase 1
tig Mar 8, 2026
2e10e13
Centralize terminal detection in Driver.IsAttachedToTerminal
tig Mar 8, 2026
a3b69a0
Merge branch 'v2_develop' of tig:tig/Terminal.Gui into v2_develop
tig Mar 8, 2026
8b7bd67
Set DisableRealDriverIO for all GitHub test jobs
tig Mar 8, 2026
06713b6
Refactor drivers: cleanup, modern C#, bug fixes
tig Mar 8, 2026
378d462
Gracefully handle no-terminal scenarios in output drivers
tig Mar 8, 2026
3c59dd3
Refactor IsAttachedToTerminal checks in Output classes
tig Mar 8, 2026
35d5ad9
Refactor terminal detection and modernize input tests
tig Mar 8, 2026
92b183f
Centralize terminal detection in Driver.IsAttachedToTerminal
tig Mar 8, 2026
6181bcf
Set DisableRealDriverIO for all GitHub test jobs
tig Mar 8, 2026
f7c6cdb
Refactor drivers: cleanup, modern C#, bug fixes
tig Mar 8, 2026
90a48d2
Gracefully handle no-terminal scenarios in output drivers
tig Mar 8, 2026
d4f1cd7
Refactor IsAttachedToTerminal checks in Output classes
tig Mar 8, 2026
e7b4d28
Refactor terminal detection and modernize input tests
tig Mar 8, 2026
efa771e
Remove kitty keyboard code from terminal detection PR
tig Mar 8, 2026
63e51c4
deleted empty test file
tig Mar 8, 2026
6cf98ac
code cleanup
tig Mar 8, 2026
046af97
Add env var and terminal tests to IntegrationTests suite
tig Mar 8, 2026
7abd734
Merge branch 'fix/4804-terminal-detection' into pr-4810
tig Mar 8, 2026
ecca89f
Update Terminal.Gui/Drivers/Output/OutputBase.cs
tig Mar 8, 2026
942318a
Update Terminal.Gui/Drivers/DotNetDriver/NetInput.cs
tig Mar 8, 2026
a3e595f
Update Tests/IntegrationTests/FluentTests/TestContextTests.cs
tig Mar 8, 2026
4cbdc09
Update Terminal.Gui/Drivers/Input/InputImpl.cs
tig Mar 8, 2026
902c2b3
Refactor tests and AnsiOutput; improve logging setup
tig Mar 8, 2026
8720f37
Merge remote-tracking branch 'upstream/fix/4804-terminal-detection' i…
tig Mar 8, 2026
ced7715
Update Terminal.Gui/Drivers/Output/OutputBase.cs
tig Mar 8, 2026
8fef93d
Refactor MainLoopCoordinatorTests to use TestLogging instead of globa…
tig Mar 8, 2026
0aa33a6
Merge branch 'fix/4804-terminal-detection' into pr-4810
tig Mar 8, 2026
448223c
Merge branch 'v2_develop' of tig:tig/Terminal.Gui into v2_develop
tig Mar 9, 2026
76aa1be
Standardize tracing and simplify input/output logic
tig Mar 9, 2026
d2cb931
Clean up logging, tracing, and property usage
tig Mar 9, 2026
f1ecbb8
Merge branch 'v2_develop' into feature/kitty
tig Mar 9, 2026
7fb84bd
Merge branch 'v2_develop' of tig:tig/Terminal.Gui into v2_develop
tig Mar 9, 2026
40fa28b
Merge branch 'v2_develop' into feature/kitty
tig Mar 9, 2026
78b2303
Merge branch 'feature/kitty' of tig:tig/Terminal.Gui into feature/kitty
tig Mar 9, 2026
6021b3f
Removed setting of global var
tig Mar 9, 2026
cb3e033
Handle degraded mode in kitty keyboard enablement
tig Mar 9, 2026
2df45a9
removed old doc
tig Mar 9, 2026
6ca7ab4
Merge branch 'gui-cs:v2_develop' into v2_develop
tig Mar 9, 2026
6ad87e1
Merge branch 'v2_develop' of tig:tig/Terminal.Gui into v2_develop
tig Mar 9, 2026
47a54aa
Merge branch 'v2_develop' into feature/kitty
tig Mar 9, 2026
fdf967c
Update kitty keyboard plan with Phase 2 implementation steps
tig Mar 9, 2026
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
28 changes: 28 additions & 0 deletions Terminal.Gui/App/MainLoop/MainLoopCoordinator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,34 @@ private void BuildDriverIfPossible (IApplication? app)
}
}

try
{
KittyKeyboardProtocolDetector kittyKeyboardDetector = new (_driver);
kittyKeyboardDetector.Detect (result =>
{
_driver.SetKittyKeyboardProtocol (result);
Trace.Lifecycle (app?.MainThreadId?.ToString (),
"KittyKeyboard",
$"Probe complete: Supported={result.IsSupported}, SupportedFlags={result.SupportedFlags}, EnabledFlags={result.EnabledFlags}");

if (!result.IsSupported || result.EnabledFlags <= 0 || _output is not AnsiOutput ansiOutput)
{
Trace.Lifecycle (app?.MainThreadId?.ToString (), "KittyKeyboard", "Kitty keyboard mode not enabled");
return;
}

ansiOutput.EnableKittyKeyboard (result.EnabledFlags);
_driver.SetKittyKeyboardEnabledFlags (ansiOutput.KittyKeyboardEnabledFlags);
Trace.Lifecycle (app?.MainThreadId?.ToString (),
"KittyKeyboard",
$"Enabled kitty keyboard flags {ansiOutput.KittyKeyboardEnabledFlags}");
});
}
catch (Exception ex)
{
Logging.Warning ($"Kitty keyboard protocol detection failed: {ex.Message}");
}

_startupSemaphore.Release ();
Logging.Trace ($"app: {app.MainThreadId} Driver: _input: {_input}, _output: {_output}");
}
Expand Down
13 changes: 2 additions & 11 deletions Terminal.Gui/Drivers/AnsiDriver/AnsiInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,6 @@ public class AnsiInput : InputImpl<char>, ITestableInput<char>
/// </summary>
public AnsiInput ()
{
//Logging.Information ($"Creating {nameof (AnsiInput)}");

_platform = AnsiPlatform.Degraded;

try
Expand Down Expand Up @@ -226,8 +224,6 @@ public override IEnumerable<char> Read ()

case AnsiPlatform.Degraded:
default:
// Logging.Trace ("IsVTModeEnabled is NOT enabled");

yield break;
}
}
Expand Down Expand Up @@ -305,8 +301,6 @@ private void FlushInput ()
// can cause ReadFile to block indefinitely.
if (_pollMap == null)
{
//Logging.Trace ("");

return;
}

Expand Down Expand Up @@ -364,12 +358,9 @@ private void FlushInput ()
}
}

// Will be called on the main loop thread.
/// <inheritdoc/>
public void InjectInput (char input) =>

//Logging.Trace ($"Enqueuing input: {input.Key}");
// Will be called on the main loop thread.
_testInput.Enqueue (input);
public void InjectInput (char input) => _testInput.Enqueue (input);

/// <inheritdoc/>
public override void Dispose ()
Expand Down
46 changes: 39 additions & 7 deletions Terminal.Gui/Drivers/AnsiDriver/AnsiOutput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ public class AnsiOutput : OutputBase, IOutput
/// </summary>
public AnsiOutput ()
{
// Logging.Information ($"Creating {nameof (AnsiOutput)}");

_platform = AnsiPlatform.Degraded;

_lastBuffer = new OutputBufferImpl ();
Expand Down Expand Up @@ -171,8 +169,6 @@ protected override void Write (StringBuilder output)
}
catch (Exception)
{
//Logging.Warning (e.Message);

// ignore for unit tests
}
}
Expand Down Expand Up @@ -206,12 +202,46 @@ public void Write (ReadOnlySpan<char> text)
}
catch (Exception)
{
//Logging.Warning (e.Message);

// ignore for unit tests
}
}

/// <summary>
/// Gets the kitty keyboard flags currently enabled on the terminal.
/// </summary>
internal int KittyKeyboardEnabledFlags { get; private set; }

/// <summary>
/// Enables kitty keyboard progressive enhancement flags for the active terminal.
/// </summary>
/// <param name="flags">The kitty keyboard flags to enable.</param>
internal void EnableKittyKeyboard (int flags)
{
if (flags <= 0 || _platform == AnsiPlatform.Degraded)
{
return;
}

Trace.Lifecycle (nameof (AnsiOutput), "KittyKeyboard", $"Writing enable sequence for flags {flags}");
Write (EscSeqUtils.CSI_EnableKittyKeyboardFlags (flags));
KittyKeyboardEnabledFlags = flags;
}

/// <summary>
/// Restores the previous kitty keyboard flag state if kitty mode was enabled.
/// </summary>
internal void DisableKittyKeyboard ()
{
if (KittyKeyboardEnabledFlags <= 0)
{
return;
}

Trace.Lifecycle (nameof (AnsiOutput), "KittyKeyboard", $"Writing disable sequence for flags {KittyKeyboardEnabledFlags}");
Write (EscSeqUtils.CSI_DisableKittyKeyboardFlags);
KittyKeyboardEnabledFlags = 0;
}

/// <inheritdoc cref="IOutput.Write(IOutputBuffer)"/>
public override void Write (IOutputBuffer buffer)
{
Expand Down Expand Up @@ -316,6 +346,8 @@ public void Dispose ()
{
try
{
DisableKittyKeyboard ();

if (_platform == AnsiPlatform.Degraded)
{
return;
Expand All @@ -333,7 +365,7 @@ public void Dispose ()
}
finally
{
//Logging.Trace ("Flushing and closing.");
Trace.Lifecycle (nameof (AnsiOutput), "Dispose", "Flushing output and releasing resources.");

_windowsVTOutput?.Dispose ();
}
Expand Down
1 change: 1 addition & 0 deletions Terminal.Gui/Drivers/AnsiHandling/AnsiKeyboardParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public class AnsiKeyboardParser
private readonly List<AnsiKeyboardParserPattern> _patterns =
[
new Ss3Pattern (),
new KittyKeyboardPattern (),
new CsiKeyPattern (),
new CsiCursorPattern (),
new EscAsAltPattern { IsLastMinute = true }
Expand Down
62 changes: 48 additions & 14 deletions Terminal.Gui/Drivers/AnsiHandling/AnsiResponseExpectation.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using System.Text.RegularExpressions;

namespace Terminal.Gui.Drivers;

/// <summary>
Expand All @@ -26,32 +24,68 @@ public bool Matches (string? cur)
return true;
}

// Remove leading ESC if present to simplify parsing
string s = cur;

if (s.Length > 0 && s [0] == '\x1B')
{
s = s [1..];
}

// Extract the first numeric token after '[' (e.g. "[8;..." -> "8", "[6;..." -> "6")
// This matches typical CSI reply formats used here.
Match m = Regex.Match (s, @"^\[(\d+);");
string? csiToken = TryGetLeadingToken (s, '[', Terminator!);

if (!string.IsNullOrEmpty (csiToken))
{
return TokenMatchesValue (csiToken, Value);
}

string? oscToken = TryGetLeadingToken (s, ']', Terminator!);

if (!string.IsNullOrEmpty (oscToken))
{
return TokenMatchesValue (oscToken, Value);
}

return s.Contains ($"[{Value};", StringComparison.Ordinal)
|| s.Contains ($"]{Value};", StringComparison.Ordinal)
|| s.Contains ($"[{Value}", StringComparison.Ordinal)
|| s.Contains ($"]{Value}", StringComparison.Ordinal);
}

private static string? TryGetLeadingToken (string input, char prefix, string terminator)
{
if (string.IsNullOrEmpty (input) || input [0] != prefix)
{
return null;
}

int startIndex = 1;
int endIndex = input.IndexOfAny ([ ';', terminator [0] ], startIndex);

if (endIndex < 0)
{
endIndex = input.Length;
}

if (m.Success)
if (endIndex <= startIndex)
{
return string.Equals (m.Groups [1].Value, Value, StringComparison.Ordinal);
return null;
}

// Extract the first numeric token after ']' for OSC responses (e.g. "]10;..." -> "10", "]11;..." -> "11")
Match oscMatch = Regex.Match (s, @"^\](\d+);");
return input [startIndex..endIndex];
}

private static bool TokenMatchesValue (string token, string value)
{
if (string.Equals (token, value, StringComparison.Ordinal))
{
return true;
}

if (oscMatch.Success)
if (!char.IsDigit (value [0]))
{
return string.Equals (oscMatch.Groups [1].Value, Value, StringComparison.Ordinal);
return token.StartsWith (value, StringComparison.Ordinal);
}

// Fallback: conservative contains check (rare)
return s.Contains ($"[{Value};", StringComparison.Ordinal) || s.Contains ($"]{Value};", StringComparison.Ordinal);
return false;
}
}
28 changes: 28 additions & 0 deletions Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,34 @@ internal static void CSI_AppendTextStyleChange (StringBuilder output, TextStyle
/// </summary>
public static readonly AnsiEscapeSequence CSI_RequestCursorPositionReport = new () { Request = CSI + "?6n", Terminator = "R" };

/// <summary>
/// ESC [ ? u - Query kitty keyboard progressive enhancement flags.
/// The terminal reply to <see cref="CSI_QueryKittyKeyboardFlags"/> is ESC [ ? flags u.
/// </summary>
public static readonly AnsiEscapeSequence CSI_QueryKittyKeyboardFlags = new () { Request = CSI + "?u", Terminator = "u", Value = "?" };

/// <summary>
/// Phase 1 enables only kitty's disambiguate escape codes flag.
/// </summary>
public const int KittyKeyboardDisambiguateEscapeCodes = 1;

/// <summary>
/// The kitty keyboard flags enabled during phase 1.
/// </summary>
public const int KittyKeyboardPhase1Flags = KittyKeyboardDisambiguateEscapeCodes;

/// <summary>
/// ESC [ &gt; flags u - Push current kitty keyboard flags and enable the specified flags.
/// </summary>
/// <param name="flags">The kitty keyboard progressive enhancement flags to enable.</param>
/// <returns>The ANSI request string.</returns>
public static string CSI_EnableKittyKeyboardFlags (int flags) => $"{CSI}>{flags}u";

/// <summary>
/// ESC [ &lt; u - Restore the previously pushed kitty keyboard flag state.
/// </summary>
public static readonly string CSI_DisableKittyKeyboardFlags = CSI + "<u";

/// <summary>
/// The terminal reply to <see cref="CSI_RequestCursorPositionReport"/>. ESC [ ? (y) ; (x) R
/// </summary>
Expand Down
Loading
Loading