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