Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #3767. Allowing any driver to request ANSI escape sequence with immediate response. #3768

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
136 commits
Select commit Hold shift + click to select a range
9de21e0
Fixes #3767. Allowing any driver to request ANSI escape sequence with…
BDisp Sep 30, 2024
0b3d219
Using a more appropriate request for cursor position.
BDisp Sep 30, 2024
c89a9c8
Add AnsiEscapeSequenceRequest and AnsiEscapeSequenceResponse classes.
BDisp Sep 30, 2024
21c3155
Prevents empty response error.
BDisp Oct 1, 2024
7142a04
Add AnsiEscapeSequenceRequests scenario.
BDisp Oct 1, 2024
dd31796
Improving scenario layout.
BDisp Oct 1, 2024
8d52fe7
Fix NetDriver read key issue.
BDisp Oct 2, 2024
1d20bce
Change file name.
BDisp Oct 2, 2024
c320fc2
Improves null dequeues handling.
BDisp Oct 2, 2024
8050202
Replace ExecuteAnsiRequest with TryParse.
BDisp Oct 5, 2024
bdc6fe6
Code cleanup.
BDisp Oct 5, 2024
922f586
Replace from TryParse to TryExecuteAnsiRequest.
BDisp Oct 5, 2024
2c76ed2
Fix exception throwing if no terminator is specified.
BDisp Oct 7, 2024
331a49e
Fix merge errors.
BDisp Oct 9, 2024
e5c30eb
Make AnsiEscapeSequenceRequest agnostic of each driver.
BDisp Oct 13, 2024
7249de0
Cannot run with unit tests.
BDisp Oct 13, 2024
f850e73
Fix unit test.
BDisp Oct 13, 2024
67d497c
Fixes CursesDriver stale buffer.
BDisp Oct 13, 2024
b35b9f5
Add abstract WriteAnsi into the ConsoleDriver for each driver handlin…
BDisp Oct 14, 2024
a2e4d82
Add WriteAnsiDefault method to common code.
BDisp Oct 14, 2024
cc1d668
Fix Window Terminal Preview using WindowsDriver.
BDisp Oct 14, 2024
5ce3996
Prevents throwing if selected item is equal to minus 1.
BDisp Oct 14, 2024
d963941
Preparing NetDriver to handle ansi response on demand.
BDisp Oct 15, 2024
0de8262
View.Mouse cleanup - WIP
tig Oct 15, 2024
c318748
View.Mouse cleanup - WIP3
tig Oct 15, 2024
2ad9887
View.Mouse cleanup - Fixed combobox
tig Oct 15, 2024
ec14b62
Merged MouseEvent and MouseEventEventArgs into MouseEventArgs
tig Oct 15, 2024
9dcfe02
Fixed Time/DateField crash
tig Oct 15, 2024
59396ae
Fixed Time/DateField crash 2
tig Oct 15, 2024
49096fa
Fix merge errors.
BDisp Oct 15, 2024
884011e
Improving WriteAnsi method to return the response.
BDisp Oct 16, 2024
9348328
Rename to WriteAnsiRequest.
BDisp Oct 16, 2024
1645d1f
Ensure dequeue on bad requests or without response in NetDriver.
BDisp Oct 16, 2024
34fb5b5
Non-blocking ReadConsoleInput in WindowsDriver.
BDisp Oct 16, 2024
8a6928e
Merge branch 'v2_develop' into v2_3767_ansi-escape-sequence-all-drivers
BDisp Oct 17, 2024
7fa098f
Remove unnecessary IsSuspendRead property.
BDisp Oct 18, 2024
9759b97
Remove unused variable.
BDisp Oct 18, 2024
1ecff5e
Fix ansi multi-thread requests handling in the NetDriver.
BDisp Oct 18, 2024
9a840dd
Remove unnecessary WriteAnsiRequestDefault method.
BDisp Oct 19, 2024
cc4f7e9
Simplifying code.
BDisp Oct 19, 2024
542d82d
Remove response from error.
BDisp Oct 19, 2024
9749142
Set _forceRead as true before set _waitForProbe.
BDisp Oct 20, 2024
9eb7023
Improves CursesDriver to allow non-blocking input polling.
BDisp Oct 20, 2024
4090ea4
Avoids response starting without Esc char on stressing tests.
BDisp Oct 20, 2024
5ef34b8
Refactoring code.
BDisp Oct 21, 2024
ee0f93d
Merge branch 'v2_develop' into v2_3767_ansi-escape-sequence-all-drivers
BDisp Oct 28, 2024
c1063a0
Remove unneeded abstract ConsoleDriver objects.
BDisp Oct 31, 2024
1b6963f
Improves a lot of console key mappings.
BDisp Oct 31, 2024
ba607f1
Commenting GetIsKeyCodeAtoZ contradiction debug check.
BDisp Oct 31, 2024
ee8d040
Fix NetDriver and WindowsDriver.
BDisp Oct 31, 2024
8ab7ec5
Fix for WindowsDriver.
BDisp Oct 31, 2024
de45b4b
Refactoring a lot CursesDriver.
BDisp Oct 31, 2024
6f25337
Cleanup unused code and comments.
BDisp Nov 1, 2024
126bcef
Add EscSeqRequests support to WindowsDriver.
BDisp Nov 1, 2024
5b39c3d
Fix unit test.
BDisp Nov 1, 2024
c89efe5
Add tab view for single/multi request sends
tznind Nov 2, 2024
0ddc11c
Add bulk send to scenario
tznind Nov 2, 2024
64ad1a9
Fix a bug which was sending the terminator to the mainloop.
BDisp Nov 2, 2024
a2872cd
Ensures a new iteration when _eventReady is already set.
BDisp Nov 2, 2024
1d58ba4
Merge pull request #201 from tznind/single-multi
BDisp Nov 2, 2024
68b41a3
Set CanFocus true and only SetNeedsDisplay if were sent or answered.
BDisp Nov 2, 2024
c999fc0
Trying fix unit tests.
BDisp Nov 2, 2024
3c4564c
Explain what the colors represent.
BDisp Nov 2, 2024
2e0bc01
Fixes #3807. WindowsDriver doesn't process characters with accents.
BDisp Nov 4, 2024
2d8bfd5
Fix comment.
BDisp Nov 4, 2024
808896d
Fix bug where some key were not been split.
BDisp Nov 4, 2024
782325f
Change to ConcurrentQueue.
BDisp Nov 4, 2024
e2f3b7b
Using empty string instead of space.
BDisp Nov 4, 2024
e35b37f
Add InvalidRequestTerminator property.
BDisp Nov 4, 2024
da21ef1
Improves drivers responses to being more reliable.
BDisp Nov 4, 2024
81eb301
Fix escSeqRequests that may be null running unit tests.
BDisp Nov 4, 2024
173f820
Disable HACK_CHECK_WINCHANGED.
BDisp Nov 4, 2024
472ec45
Remove _screenBuffer and only using the alternate buffer.
BDisp Nov 5, 2024
ad4f6c4
Fix unit test.
BDisp Nov 5, 2024
f73c817
Fix unit tests.
BDisp Nov 5, 2024
7921ae3
Returns ansiRequest.Response instead of a variable for more thread safe.
BDisp Nov 5, 2024
ecbbed0
For re-run CI.
BDisp Nov 5, 2024
2919d55
Code Review
tig Nov 6, 2024
4ac79c5
Merge pull request #203 from tig/BDisp-v2_3767_ansi-escape-sequence-a…
BDisp Nov 6, 2024
7d9ae7f
Ad more unit test to handling with IsLetterOrDigit, IsPunctuation and…
BDisp Nov 6, 2024
eb98707
Revert Key class changes.
BDisp Nov 6, 2024
0b11e20
Change Response to a nullable string.
BDisp Nov 6, 2024
bdcc0ff
Return null instead if empty and fix a bug that was returning the ter…
BDisp Nov 6, 2024
629cea8
Handling null response.
BDisp Nov 7, 2024
f87c2b1
Rename EscSeq to AnsiEscapeSequence and move to his folder.
BDisp Nov 7, 2024
dba089f
Change category.
BDisp Nov 7, 2024
5efba6a
Code cleanup.
BDisp Nov 7, 2024
b7b9e01
Fix error with Key.Space using lowercase letters.
BDisp Nov 7, 2024
a64f68c
Moving MapKey method to the AnsiEscapeSequenceRequestUtils class.
BDisp Nov 7, 2024
cc21bd4
Fix a bug where a esc key was ignored.
BDisp Nov 7, 2024
3e952df
Change to BlockingCollection, thanks to @tznind.
BDisp Nov 7, 2024
44d5997
Split more WindowDriver classes and #nullable enable.
BDisp Nov 7, 2024
873b578
#nullable enable.
BDisp Nov 7, 2024
ea0cacf
There is no null values here.
BDisp Nov 7, 2024
2124ad3
Cleanup code.
BDisp Nov 7, 2024
a6af3aa
An huge improvements on drivers and bug fixes.
BDisp Nov 8, 2024
55e5944
Merge branch 'v2_develop' into v2_3767_ansi-escape-sequence-all-drivers
BDisp Nov 8, 2024
8e9bb95
Code cleanup.
BDisp Nov 8, 2024
012d47e
Fix IncompleteCkInfos bug.
BDisp Nov 8, 2024
8c5832f
Fix NetDriver to also work well in Linux.
BDisp Nov 9, 2024
c6512e6
Finally fixed IncompleteCkInfos handling in NetDriver.
BDisp Nov 9, 2024
c8aac60
Add handling error in the scenario.
BDisp Nov 9, 2024
1680ec5
Make AnsiEscapeSequenceRequests static.
BDisp Nov 10, 2024
6444cc0
Fix ansi responses not being handling in WSL.
BDisp Nov 10, 2024
5e2b611
Resolving merge conflicts.
BDisp Nov 10, 2024
88f4045
Fix merge errors.
BDisp Nov 10, 2024
71fb38d
Add unit test for Screen.
BDisp Nov 10, 2024
0f6ce06
Fix SetCursorVisibility in CursesDriver.
BDisp Nov 10, 2024
f51fbbe
Forgot Shutdown in unit test.
BDisp Nov 10, 2024
089f756
Simplify request responses handling.
BDisp Nov 10, 2024
36876b9
Change TryWriteAnsiRequest method to virtual which use common code fo…
BDisp Nov 11, 2024
7d146b7
Fix unit test which was causing With_Subview_Using_PosFunc failing.
BDisp Nov 11, 2024
3425c5a
Forgot to use _waitForInput.Reset and remove unnecessary finally block.
BDisp Nov 11, 2024
644afa9
Change filter in the ansi request scenario.
BDisp Nov 11, 2024
1b830bd
Cleanup code.
BDisp Nov 11, 2024
1795547
ProcessInputQueue must be outside the wait block.
BDisp Nov 11, 2024
70e91f2
Tidy up code.
BDisp Nov 11, 2024
967cbc2
Switch to ConcurrentQueue for all drivers.
BDisp Nov 11, 2024
ae1bdd1
Fix a bug where for e.g. two 'c' terminator were included in the resp…
BDisp Nov 11, 2024
6bac652
Fix ansiSequence bug not being clear after finish get the response wh…
BDisp Nov 11, 2024
d31f43d
Add ObjectDisposedException to the catch block.
BDisp Nov 11, 2024
7cd8440
Switch to IMainLoopDriver._waitForInput.
BDisp Nov 11, 2024
54f6264
Add ProcessAnsiRequestHandler method task.
BDisp Nov 11, 2024
a3c961e
Prevents an alone escape being eating by the response.
BDisp Nov 11, 2024
5b76f4d
Remove the answer from the _sends variable. Normally it's always zero.
BDisp Nov 12, 2024
53ea0a8
Replace to ForceRead and to WaitForInput.
BDisp Nov 13, 2024
be1dc80
Fix comment.
BDisp Nov 13, 2024
f0b2474
Code cleanup.
BDisp Nov 13, 2024
efba784
Using _timeoutsLockToken instead of the _timeouts for all.
BDisp Nov 13, 2024
302a7cf
Decrease interval if there is more sent requests.
BDisp Nov 13, 2024
ce0183f
Avoids exit after read the response with empty key.
BDisp Nov 17, 2024
a649b12
Rename to _ansiResponseReady.
BDisp Nov 17, 2024
c06c157
Tidying up layout.
BDisp Nov 17, 2024
1ea9ae0
Handles error on request values.
BDisp Nov 17, 2024
99350b7
Add local function to clear input.
BDisp Nov 17, 2024
e30200d
Remove duplicate wait.
BDisp Nov 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Terminal.Gui/Application/Application.Run.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#nullable enable
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;

namespace Terminal.Gui;

Expand Down Expand Up @@ -522,7 +521,7 @@ public static void LayoutAndDraw (bool forceDraw = false)

/// <summary>The <see cref="MainLoop"/> driver for the application</summary>
/// <value>The main loop.</value>
internal static MainLoop? MainLoop { get; private set; }
public static MainLoop? MainLoop { get; private set; }

/// <summary>
/// Set to true to cause <see cref="End"/> to be called after the first iteration. Set to false (the default) to
Expand Down
27 changes: 17 additions & 10 deletions Terminal.Gui/Application/MainLoop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
namespace Terminal.Gui;

/// <summary>Interface to create a platform specific <see cref="MainLoop"/> driver.</summary>
internal interface IMainLoopDriver
public interface IMainLoopDriver
{
/// <summary>Must report whether there are any events pending, or even block waiting for events.</summary>
/// <returns><c>true</c>, if there were pending events, <c>false</c> otherwise.</returns>
Expand All @@ -29,14 +29,24 @@ internal interface IMainLoopDriver

/// <summary>Wakes up the <see cref="MainLoop"/> that might be waiting on input, must be thread safe.</summary>
void Wakeup ();

/// <summary>
/// Flag <see cref="WaitForInput"/> to force reading input instead of call <see cref="ManualResetEventSlim.Wait ()"/>.
/// </summary>
bool ForceRead { get; set; }

/// <summary>
/// Switch for waiting for input or signaled to resume.
/// </summary>
ManualResetEventSlim WaitForInput { get; set; }
}

/// <summary>The MainLoop monitors timers and idle handlers.</summary>
/// <remarks>
/// Monitoring of file descriptors is only available on Unix, there does not seem to be a way of supporting this
/// on Windows.
/// </remarks>
internal class MainLoop : IDisposable
public class MainLoop : IDisposable
{
internal List<Func<bool>> _idleHandlers = new ();
internal SortedList<long, Timeout> _timeouts = new ();
Expand Down Expand Up @@ -72,7 +82,7 @@ internal ReadOnlyCollection<Func<bool>> IdleHandlers

/// <summary>The current <see cref="IMainLoopDriver"/> in use.</summary>
/// <value>The main loop driver.</value>
internal IMainLoopDriver MainLoopDriver { get; private set; }
public IMainLoopDriver MainLoopDriver { get; private set; }

/// <summary>Used for unit tests.</summary>
internal bool Running { get; set; }
Expand Down Expand Up @@ -130,10 +140,7 @@ internal Func<bool> AddIdle (Func<bool> idleHandler)
/// </remarks>
internal object AddTimeout (TimeSpan time, Func<bool> callback)
{
if (callback is null)
{
throw new ArgumentNullException (nameof (callback));
}
ArgumentNullException.ThrowIfNull (callback);

var timeout = new Timeout { Span = time, Callback = callback };
AddTimeout (time, timeout);
Expand All @@ -156,7 +163,7 @@ internal bool CheckTimersAndIdleHandlers (out int waitTimeout)

waitTimeout = 0;

lock (_timeouts)
lock (_timeoutsLockToken)
{
if (_timeouts.Count > 0)
{
Expand Down Expand Up @@ -262,7 +269,7 @@ internal void Run ()
/// </remarks>
internal void RunIteration ()
{
lock (_timeouts)
lock (_timeoutsLockToken)
{
if (_timeouts.Count > 0)
{
Expand All @@ -272,7 +279,7 @@ internal void RunIteration ()

MainLoopDriver.Iteration ();

var runIdle = false;
bool runIdle;

lock (_idleHandlersLock)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#nullable enable
namespace Terminal.Gui;

/// <summary>
/// Describes an ongoing ANSI request sent to the console.
/// Send a request using <see cref="ConsoleDriver.TryWriteAnsiRequest"/> which will return the response.
/// </summary>
public class AnsiEscapeSequenceRequest
{
internal readonly object _responseLock = new (); // Per-instance lock

/// <summary>
/// Gets the response received from the request.
/// </summary>
public AnsiEscapeSequenceResponse? AnsiEscapeSequenceResponse { get; internal set; }

/// <summary>
/// The value expected in the response after the CSI e.g.
/// <see>
/// <cref>EscSeqUtils.CSI_ReportTerminalSizeInChars.Value</cref>
/// </see>
/// should result in a response of the form <c>ESC [ 8 ; height ; width t</c>. In this case,
/// <see cref="ExpectedResponseValue"/>
/// will be <c>"8"</c>.
/// </summary>
public string? ExpectedResponseValue { get; init; }

/// <summary>
/// Gets the request string to send e.g. see
/// <see>
/// <cref>EscSeqUtils.CSI_SendDeviceAttributes.Request</cref>
/// </see>
/// </summary>
public required string Request { get; init; }

/// <summary>
/// <para>
/// Gets the terminator that uniquely identifies the response received from
/// the console. e.g. for
/// <see>
/// <cref>EscSeqUtils.CSI_SendDeviceAttributes.Request</cref>
/// </see>
/// the terminator is
/// <see>
/// <cref>EscSeqUtils.CSI_SendDeviceAttributes.Terminator</cref>
/// </see>
/// .
/// </para>
/// <para>
/// After sending a request, the first response with matching terminator will be matched
/// to the oldest outstanding request.
/// </para>
/// </summary>
public required string Terminator { get; init; }

internal void RaiseResponseFromInput (string? response, AnsiEscapeSequenceRequest? request)
{
ProcessResponse (response, request);

ResponseFromInput?.Invoke (this, AnsiEscapeSequenceResponse);
}

/// <summary>
/// Raised with the response object and validation.
/// </summary>
internal event EventHandler<AnsiEscapeSequenceResponse?>? ResponseFromInput;

/// <summary>
/// Process the <see cref="AnsiEscapeSequenceResponse"/> of an ANSI escape sequence request.
/// </summary>
/// <param name="response">The response.</param>
private void ProcessResponse (string? response, AnsiEscapeSequenceRequest? request)
{
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 ());
}

if (request is { } && !string.IsNullOrEmpty (request.ExpectedResponseValue) && request.ExpectedResponseValue != values [0])
{
error.AppendLine ($"Error executing ANSI request:\nValue ends with '{values [0]}'\nand doesn't end with: '{ExpectedResponseValue! [^1]}'");
}
}

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)
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#nullable enable
namespace Terminal.Gui;

/// <summary>
/// Represents the status of an ANSI escape sequence request made to the terminal using
/// <see cref="AnsiEscapeSequenceRequests"/>.
/// </summary>
/// <remarks></remarks>
public class AnsiEscapeSequenceRequestStatus
{
/// <summary>Creates a new state of escape sequence request.</summary>
/// <param name="ansiRequest">The <see cref="AnsiEscapeSequenceRequest"/> object.</param>
public AnsiEscapeSequenceRequestStatus (AnsiEscapeSequenceRequest ansiRequest) { AnsiRequest = ansiRequest; }

/// <summary>Gets the Escape Sequence Terminator (e.g. ESC[8t ... t is the terminator).</summary>
public AnsiEscapeSequenceRequest AnsiRequest { get; }
}
Loading
Loading