#nullable enable
namespace Terminal.Gui;
///
/// Describes an ongoing ANSI request sent to the console.
/// Use to handle the response
/// when console answers the request.
///
public class AnsiEscapeSequenceRequest
{
///
/// Request to send e.g. see
///
/// EscSeqUtils.CSI_SendDeviceAttributes.Request
///
///
public required string Request { get; init; }
///
/// Response received from the request.
///
public string Response { get; internal set; } = string.Empty;
///
/// Invoked when the console responds with an ANSI response code that matches the
///
///
public event EventHandler? ResponseReceived;
///
///
/// The terminator that uniquely identifies the type of response as responded
/// by the console. e.g. for
///
/// EscSeqUtils.CSI_SendDeviceAttributes.Request
///
/// the terminator is
///
/// EscSeqUtils.CSI_SendDeviceAttributes.Terminator
///
/// .
///
///
/// After sending a request, the first response with matching terminator will be matched
/// to the oldest outstanding request.
///
///
public required string Terminator { get; init; }
///
/// Execute an ANSI escape sequence escape which may return a response or error.
///
/// The ANSI escape sequence to request.
///
/// When this method returns , an object containing the response with an empty
/// error.
///
/// A with the response, error, terminator and value.
public static bool TryExecuteAnsiRequest (AnsiEscapeSequenceRequest ansiRequest, out AnsiEscapeSequenceResponse result)
{
var error = new StringBuilder ();
var savedIsReportingMouseMoves = false;
ConsoleDriver? driver = null;
var values = new string? [] { null };
try
{
driver = Application.Driver;
savedIsReportingMouseMoves = driver!.IsReportingMouseMoves;
if (savedIsReportingMouseMoves)
{
driver.StopReportingMouseMoves ();
}
driver!.IsSuspendRead = true;
// Send the ANSI escape sequence
ansiRequest.Response = driver.WriteAnsiRequest (ansiRequest);
if (!string.IsNullOrEmpty (ansiRequest.Response) && !ansiRequest.Response.StartsWith (EscSeqUtils.KeyEsc))
{
throw new InvalidOperationException ("Invalid escape character!");
}
if (string.IsNullOrEmpty (ansiRequest.Terminator))
{
throw new InvalidOperationException ("Terminator request is empty.");
}
if (!ansiRequest.Response.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 (string.IsNullOrEmpty (error.ToString ()))
{
(string? c1Control, string? code, values, string? terminator) = EscSeqUtils.GetEscapeResult (ansiRequest.Response.ToCharArray ());
}
if (savedIsReportingMouseMoves)
{
driver!.IsSuspendRead = false;
driver.StartReportingMouseMoves ();
}
}
AnsiEscapeSequenceResponse ansiResponse = new ()
{
Response = ansiRequest.Response, Error = error.ToString (),
Terminator = string.IsNullOrEmpty (ansiRequest.Response) ? "" : ansiRequest.Response [^1].ToString (), Value = values [0]
};
// Invoke the event if it's subscribed
ansiRequest.ResponseReceived?.Invoke (ansiRequest, ansiResponse);
result = ansiResponse;
return string.IsNullOrWhiteSpace (result.Error) && !string.IsNullOrWhiteSpace (result.Response);
}
///
/// The value expected in the response e.g.
///
/// EscSeqUtils.CSI_ReportTerminalSizeInChars.Value
///
/// which will have a 't' as terminator but also other different request may return the same terminator with a
/// different value.
///
public string? Value { get; init; }
}