forked from gui-cs/Terminal.Gui
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This work by @BDisp enables us to detect sixel support on demand
- Loading branch information
Showing
13 changed files
with
504 additions
and
112 deletions.
There are no files selected for viewing
186 changes: 186 additions & 0 deletions
186
Terminal.Gui/ConsoleDrivers/AnsiEscapeSequence/AnsiEscapeSequenceRequest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
#nullable enable | ||
namespace Terminal.Gui; | ||
|
||
/// <summary> | ||
/// Describes an ongoing ANSI request sent to the console. | ||
/// Use <see cref="ResponseReceived"/> to handle the response | ||
/// when console answers the request. | ||
/// </summary> | ||
public class AnsiEscapeSequenceRequest | ||
{ | ||
/// <summary> | ||
/// Execute an ANSI escape sequence escape which may return a response or error. | ||
/// </summary> | ||
/// <param name="ansiRequest">The ANSI escape sequence to request.</param> | ||
/// <returns>A <see cref="AnsiEscapeSequenceResponse"/> with the response, error, terminator and value.</returns> | ||
public static AnsiEscapeSequenceResponse ExecuteAnsiRequest (AnsiEscapeSequenceRequest ansiRequest) | ||
{ | ||
var response = new StringBuilder (); | ||
var error = new StringBuilder (); | ||
var savedIsReportingMouseMoves = false; | ||
NetDriver? netDriver = null; | ||
|
||
try | ||
{ | ||
switch (Application.Driver) | ||
{ | ||
case NetDriver: | ||
netDriver = Application.Driver as NetDriver; | ||
savedIsReportingMouseMoves = netDriver!.IsReportingMouseMoves; | ||
|
||
if (savedIsReportingMouseMoves) | ||
{ | ||
netDriver.StopReportingMouseMoves (); | ||
} | ||
|
||
while (Console.KeyAvailable) | ||
{ | ||
netDriver._mainLoopDriver._netEvents._waitForStart.Set (); | ||
netDriver._mainLoopDriver._netEvents._waitForStart.Reset (); | ||
|
||
netDriver._mainLoopDriver._netEvents._forceRead = true; | ||
} | ||
|
||
netDriver._mainLoopDriver._netEvents._forceRead = false; | ||
|
||
break; | ||
case CursesDriver cursesDriver: | ||
savedIsReportingMouseMoves = cursesDriver.IsReportingMouseMoves; | ||
|
||
if (savedIsReportingMouseMoves) | ||
{ | ||
cursesDriver.StopReportingMouseMoves (); | ||
} | ||
|
||
break; | ||
} | ||
|
||
if (netDriver is { }) | ||
{ | ||
NetEvents._suspendRead = true; | ||
} | ||
else | ||
{ | ||
Thread.Sleep (100); // Allow time for mouse stopping and to flush the input buffer | ||
|
||
// Flush the input buffer to avoid reading stale input | ||
while (Console.KeyAvailable) | ||
{ | ||
Console.ReadKey (true); | ||
} | ||
} | ||
|
||
// Send the ANSI escape sequence | ||
Console.Write (ansiRequest.Request); | ||
Console.Out.Flush (); // Ensure the request is sent | ||
|
||
// Read the response from stdin (response should come back as input) | ||
Thread.Sleep (100); // Allow time for the terminal to respond | ||
|
||
// Read input until no more characters are available or the terminator is encountered | ||
while (Console.KeyAvailable) | ||
{ | ||
// Peek the next key | ||
ConsoleKeyInfo keyInfo = Console.ReadKey (true); // true to not display on the console | ||
|
||
// Append the current key to the response | ||
response.Append (keyInfo.KeyChar); | ||
|
||
if (keyInfo.KeyChar == ansiRequest.Terminator [^1]) // Check if the key is terminator (ANSI escape sequence ends) | ||
{ | ||
// Break out of the loop when terminator is found | ||
break; | ||
} | ||
} | ||
|
||
if (!response.ToString ().EndsWith (ansiRequest.Terminator [^1])) | ||
{ | ||
throw new InvalidOperationException ($"Terminator doesn't ends with: '{ansiRequest.Terminator [^1]}'"); | ||
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
error.AppendLine ($"Error executing ANSI request: {ex.Message}"); | ||
} | ||
finally | ||
{ | ||
if (savedIsReportingMouseMoves) | ||
{ | ||
switch (Application.Driver) | ||
{ | ||
case NetDriver: | ||
NetEvents._suspendRead = false; | ||
netDriver!.StartReportingMouseMoves (); | ||
|
||
break; | ||
case CursesDriver cursesDriver: | ||
cursesDriver.StartReportingMouseMoves (); | ||
|
||
break; | ||
} | ||
} | ||
} | ||
|
||
var values = new string? [] { null }; | ||
|
||
if (string.IsNullOrEmpty (error.ToString ())) | ||
{ | ||
(string? c1Control, string? code, values, string? terminator) = EscSeqUtils.GetEscapeResult (response.ToString ().ToCharArray ()); | ||
} | ||
|
||
AnsiEscapeSequenceResponse ansiResponse = new () | ||
{ | ||
Response = response.ToString (), Error = error.ToString (), | ||
Terminator = string.IsNullOrEmpty (response.ToString ()) ? "" : response.ToString () [^1].ToString (), Value = values [0] | ||
}; | ||
|
||
// Invoke the event if it's subscribed | ||
ansiRequest.ResponseReceived?.Invoke (ansiRequest, ansiResponse); | ||
|
||
return ansiResponse; | ||
} | ||
|
||
/// <summary> | ||
/// Request to send e.g. see | ||
/// <see> | ||
/// <cref>EscSeqUtils.CSI_SendDeviceAttributes.Request</cref> | ||
/// </see> | ||
/// </summary> | ||
public required string Request { get; init; } | ||
|
||
/// <summary> | ||
/// Invoked when the console responds with an ANSI response code that matches the | ||
/// <see cref="Terminator"/> | ||
/// </summary> | ||
public event EventHandler<AnsiEscapeSequenceResponse>? ResponseReceived; | ||
|
||
/// <summary> | ||
/// <para> | ||
/// The terminator that uniquely identifies the type of response as responded | ||
/// by the console. e.g. for | ||
/// <see> | ||
/// <cref>EscSeqUtils.CSI_SendDeviceAttributes.Request</cref> | ||
/// </see> | ||
/// the terminator is | ||
/// <see> | ||
/// <cref>EscSeqUtils.CSI_SendDeviceAttributes.Terminator</cref> | ||
/// </see> | ||
/// . | ||
/// </para> | ||
/// <para> | ||
/// After sending a request, the first response with matching terminator will be matched | ||
/// to the oldest outstanding request. | ||
/// </para> | ||
/// </summary> | ||
public required string Terminator { get; init; } | ||
|
||
/// <summary> | ||
/// The value expected in the response e.g. | ||
/// <see> | ||
/// <cref>EscSeqUtils.CSI_ReportTerminalSizeInChars.Value</cref> | ||
/// </see> | ||
/// which will have a 't' as terminator but also other different request may return the same terminator with a | ||
/// different value. | ||
/// </summary> | ||
public string? Value { get; init; } | ||
} |
53 changes: 53 additions & 0 deletions
53
Terminal.Gui/ConsoleDrivers/AnsiEscapeSequence/AnsiEscapeSequenceResponse.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
#nullable enable | ||
namespace Terminal.Gui; | ||
|
||
/// <summary> | ||
/// Describes a finished ANSI received from the console. | ||
/// </summary> | ||
public class AnsiEscapeSequenceResponse | ||
{ | ||
/// <summary> | ||
/// Error received from e.g. see | ||
/// <see> | ||
/// <cref>EscSeqUtils.CSI_SendDeviceAttributes.Request</cref> | ||
/// </see> | ||
/// </summary> | ||
public required string Error { get; init; } | ||
|
||
/// <summary> | ||
/// Response received from e.g. see | ||
/// <see> | ||
/// <cref>EscSeqUtils.CSI_SendDeviceAttributes.Request</cref> | ||
/// </see> | ||
/// . | ||
/// </summary> | ||
public required string Response { get; init; } | ||
|
||
/// <summary> | ||
/// <para> | ||
/// The terminator that uniquely identifies the type of response as responded | ||
/// by the console. e.g. for | ||
/// <see> | ||
/// <cref>EscSeqUtils.CSI_SendDeviceAttributes.Request</cref> | ||
/// </see> | ||
/// the terminator is | ||
/// <see> | ||
/// <cref>EscSeqUtils.CSI_SendDeviceAttributes.Terminator</cref> | ||
/// </see> | ||
/// </para> | ||
/// <para> | ||
/// The received terminator must match to the terminator sent by the request. | ||
/// </para> | ||
/// </summary> | ||
public required string Terminator { get; init; } | ||
|
||
/// <summary> | ||
/// The value expected in the response e.g. | ||
/// <see> | ||
/// <cref>EscSeqUtils.CSI_ReportTerminalSizeInChars.Value</cref> | ||
/// </see> | ||
/// which will have a 't' as terminator but also other different request may return the same terminator with a | ||
/// different value. | ||
/// </summary> | ||
public string? Value { get; init; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.