#nullable enable namespace Terminal.Gui; /// /// Describes an ongoing ANSI request sent to the console. /// Send a request using which will return the response. /// public class AnsiEscapeSequenceRequest { internal readonly object _responseLock = new (); // Per-instance lock /// /// Gets the response received from the request. /// public AnsiEscapeSequenceResponse? AnsiEscapeSequenceResponse { get; internal set; } /// /// 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; } /// /// Gets the request string to send e.g. see /// /// EscSeqUtils.CSI_SendDeviceAttributes.Request /// /// public required string Request { get; init; } /// /// /// 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; } internal void RaiseResponseFromInput (string? response) { ProcessResponse (response); ResponseFromInput?.Invoke (this, AnsiEscapeSequenceResponse); } /// /// Raised with the response object and validation. /// internal event EventHandler? ResponseFromInput; /// /// Process the of an ANSI escape sequence request. /// /// The response. private void ProcessResponse (string? response) { var error = new StringBuilder (); var values = new string? [] { null }; try { if (!string.IsNullOrEmpty (response) && !response.StartsWith (AnsiEscapeSequenceRequestUtils.KeyEsc)) { throw new InvalidOperationException ($"Invalid Response: {response}"); } if (string.IsNullOrEmpty (Terminator)) { throw new InvalidOperationException ("Terminator request is empty."); } if (string.IsNullOrEmpty (response)) { throw new InvalidOperationException ("Response request is null."); } if (!string.IsNullOrEmpty (response) && !response.EndsWith (Terminator [^1])) { string resp = string.IsNullOrEmpty (response) ? "" : response.Last ().ToString (); throw new InvalidOperationException ($"Terminator ends with '{resp}'\nand doesn't end with: '{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 (response?.ToCharArray ()); } } AnsiEscapeSequenceResponse = new () { Response = response, Error = error.ToString (), Terminator = string.IsNullOrEmpty (response) ? "" : response [^1].ToString (), ExpectedResponseValue = values [0], Valid = string.IsNullOrWhiteSpace (error.ToString ()) && !string.IsNullOrWhiteSpace (response) }; } }