#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; }