#nullable enable
namespace Terminal.Gui;
///
/// Describes an ongoing ANSI request sent to the console.
/// Use to handle the response
/// when the console answers the request.
///
public class AnsiEscapeSequenceRequest
{
internal readonly object _responseLock = new (); // Per-instance lock
///
/// Gets the request string to send e.g. see
///
/// EscSeqUtils.CSI_SendDeviceAttributes.Request
///
///
public required string Request { get; init; }
// QUESTION: Could the type of this propperty be AnsiEscapeSequenceResponse? This would remove the
// QUESTION: removal of the redundant Rresponse, Terminator, and ExpectedRespnseValue properties from this class?
// QUESTION: Does string.Empty indicate no response recevied? If not, perhaps make this property nullable?
///
/// Gets the response received from the request.
///
public string? Response { get; internal set; }
///
/// Raised when the console responds with an ANSI response code that matches the
///
///
public event EventHandler? ResponseReceived;
///
///
/// Gets the terminator that uniquely identifies the response received from
/// 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; }
///
/// Attempt an ANSI escape sequence request which may return a response or error.
///
/// The ANSI escape sequence to request.
///
/// When this method returns , the response. will
/// be .
///
/// A with the response, error, terminator, and value.
public static bool TryRequest (AnsiEscapeSequenceRequest ansiRequest, out AnsiEscapeSequenceResponse result)
{
var error = new StringBuilder ();
var values = new string? [] { null };
try
{
ConsoleDriver? driver = Application.Driver;
// Send the ANSI escape sequence
ansiRequest.Response = driver?.WriteAnsiRequest (ansiRequest)!;
if (!string.IsNullOrEmpty (ansiRequest.Response) && !ansiRequest.Response.StartsWith (AnsiEscapeSequenceRequestUtils.KeyEsc))
{
throw new InvalidOperationException ($"Invalid Response: {ansiRequest.Response}");
}
if (string.IsNullOrEmpty (ansiRequest.Terminator))
{
throw new InvalidOperationException ("Terminator request is empty.");
}
if (string.IsNullOrEmpty (ansiRequest.Response))
{
throw new InvalidOperationException ("Response request is null.");
}
if (!string.IsNullOrEmpty (ansiRequest.Response) && !ansiRequest.Response.EndsWith (ansiRequest.Terminator [^1]))
{
string resp = string.IsNullOrEmpty (ansiRequest.Response) ? "" : ansiRequest.Response.Last ().ToString ();
throw new InvalidOperationException ($"Terminator ends with '{resp}'\nand doesn't end with: '{ansiRequest.Terminator [^1]}'");
}
}
catch (Exception ex)
{
error.AppendLine ($"Error executing ANSI request:\n{ex.Message}");
}
finally
{
if (string.IsNullOrEmpty (error.ToString ()))
{
(string? _, string? _, values, string? _) = AnsiEscapeSequenceRequestUtils.GetEscapeResult (ansiRequest.Response.ToCharArray ());
}
}
AnsiEscapeSequenceResponse ansiResponse = new ()
{
Response = ansiRequest.Response, Error = error.ToString (),
Terminator = string.IsNullOrEmpty (ansiRequest.Response) ? "" : ansiRequest.Response [^1].ToString (), ExpectedResponseValue = 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 after the CSI e.g.
///
/// EscSeqUtils.CSI_ReportTerminalSizeInChars.Value
///
/// should result in a response of the form ESC [ 8 ; height ; width t. In this case,
/// will be "8".
///
public string? ExpectedResponseValue { get; init; }
internal void RaiseResponseFromInput (AnsiEscapeSequenceRequest ansiRequest, string? response) { ResponseFromInput?.Invoke (ansiRequest, response); }
// QUESTION: What is this for? Please provide a descriptive comment.
internal event EventHandler? ResponseFromInput;
}