Skip to content

Commit

Permalink
Fixes gui-cs#2803. Harden EscSeqUtils
Browse files Browse the repository at this point in the history
  • Loading branch information
BDisp committed Aug 22, 2023
1 parent 82b1dfb commit a3cb397
Showing 1 changed file with 83 additions and 30 deletions.
113 changes: 83 additions & 30 deletions Terminal.Gui/ConsoleDrivers/NetDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,17 @@ public void Cleanup ()
static extern uint GetLastError ();
}

internal class NetEvents {
internal class NetEvents : IDisposable {
ManualResetEventSlim _inputReady = new ManualResetEventSlim (false);
ManualResetEventSlim _waitForStart = new ManualResetEventSlim (false);
ManualResetEventSlim _winChange = new ManualResetEventSlim (false);
Queue<InputResult?> _inputResultQueue = new Queue<InputResult?> ();
ConsoleDriver _consoleDriver;
volatile ConsoleKeyInfo [] _cki = null;
volatile static bool _isEscSeq;
bool _stopTasks;
ConsoleKeyInfo [] _cki;
bool _isEscSeq;
CancellationTokenSource _cancellationTokenSource;
CancellationToken _cancellationToken;

#if PROCESS_REQUEST
bool _neededProcessRequest;
#endif
Expand All @@ -120,19 +122,16 @@ internal class NetEvents {
public NetEvents (ConsoleDriver consoleDriver)
{
_consoleDriver = consoleDriver ?? throw new ArgumentNullException (nameof (consoleDriver));
_cancellationTokenSource = new CancellationTokenSource ();
_cancellationToken = _cancellationTokenSource.Token;
Task.Run (ProcessInputResultQueue);
Task.Run (CheckWindowSizeChange);
}

internal void StopTasks ()
{
_stopTasks = true;
}

public InputResult? ReadConsoleInput ()
{
while (true) {
if (_stopTasks) {
if (_cancellationToken.IsCancellationRequested) {
return null;
}
_waitForStart.Set ();
Expand All @@ -151,6 +150,24 @@ internal void StopTasks ()
}
}

static ConsoleKeyInfo ReadConsoleKeyInfo (CancellationToken cancellationToken, bool intercept = true)
{
// if there is a key available, return it without waiting
// (or dispatching work to the thread queue)
if (Console.KeyAvailable) {
return Console.ReadKey (intercept);
}

while (!cancellationToken.IsCancellationRequested) {
Task.Delay (100);
if (Console.KeyAvailable) {
return Console.ReadKey (intercept);
}
}
cancellationToken.ThrowIfCancellationRequested ();
return default;
}

void ProcessInputResultQueue ()
{
while (true) {
Expand All @@ -163,9 +180,18 @@ void ProcessInputResultQueue ()
ConsoleKeyInfo newConsoleKeyInfo = default;

while (true) {
ConsoleKeyInfo consoleKeyInfo = Console.ReadKey (true);
if (_cancellationToken.IsCancellationRequested) {
return;
}
ConsoleKeyInfo consoleKeyInfo;
try {
consoleKeyInfo = ReadConsoleKeyInfo (_cancellationToken, true);
} catch (OperationCanceledException) {
return;
}
if ((consoleKeyInfo.KeyChar == (char)Key.Esc && !_isEscSeq)
|| (consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq)) {
|| (consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq)) {

if (_cki == null && consoleKeyInfo.KeyChar != (char)Key.Esc && _isEscSeq) {
_cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)Key.Esc, 0,
false, false, false), _cki);
Expand All @@ -179,37 +205,42 @@ void ProcessInputResultQueue ()
_isEscSeq = false;
break;
} else if (consoleKeyInfo.KeyChar == (char)Key.Esc && _isEscSeq && _cki != null) {
if (_cki != null) {
ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod);
_cki = null;
ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod);
_cki = null;
if (Console.KeyAvailable) {
_cki = EscSeqUtils.ResizeArray (consoleKeyInfo, _cki);
} else {
ProcessMapConsoleKeyInfo (consoleKeyInfo);
}
break;
} else {
_inputResultQueue.Enqueue (new InputResult {
EventType = EventType.Key,
ConsoleKeyInfo = EscSeqUtils.MapConsoleKeyInfo (consoleKeyInfo)
});
_isEscSeq = false;
ProcessMapConsoleKeyInfo (consoleKeyInfo);
break;
}
}
}

_inputReady.Set ();
}

void ProcessMapConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
{
_inputResultQueue.Enqueue (new InputResult {
EventType = EventType.Key,
ConsoleKeyInfo = EscSeqUtils.MapConsoleKeyInfo (consoleKeyInfo)
});
_isEscSeq = false;
}
}

void CheckWindowSizeChange ()
{
void RequestWindowSize ()
void RequestWindowSize (CancellationToken cancellationToken)
{
while (true) {
while (!cancellationToken.IsCancellationRequested) {
// Wait for a while then check if screen has changed sizes
Task.Delay (500).Wait ();
Task.Delay (500, cancellationToken);

if (_stopTasks) {
return;
}
int buffHeight, buffWidth;
if (((NetDriver)_consoleDriver).IsWinPlatform) {
buffHeight = Math.Max (Console.BufferHeight, 0);
Expand All @@ -227,15 +258,20 @@ void RequestWindowSize ()
return;
}
}
cancellationToken.ThrowIfCancellationRequested ();
}

while (true) {
if (_stopTasks) {
if (_cancellationToken.IsCancellationRequested) {
return;
}
_winChange.Wait ();
_winChange.Reset ();
RequestWindowSize ();
try {
RequestWindowSize (_cancellationToken);
} catch (OperationCanceledException) {
return;
}
_inputReady.Set ();
}
}
Expand Down Expand Up @@ -536,6 +572,23 @@ void HandleKeyboardEvent (ConsoleKeyInfo cki)

_inputResultQueue.Enqueue (inputResult);
}

public void Dispose ()
{
_cancellationTokenSource.Cancel ();
_cancellationTokenSource.Dispose ();
_cancellationTokenSource = null;
FlushIn ();
}

void FlushIn ()
{
// throws away any typeahead that has been typed by
// the user and has not yet been read by the program.
while (Console.KeyAvailable) {
Console.ReadKey (true);
}
}
}

internal class NetDriver : ConsoleDriver {
Expand Down Expand Up @@ -563,7 +616,7 @@ internal class NetDriver : ConsoleDriver {

public override void End ()
{
_mainLoop?._netEvents.StopTasks ();
_mainLoop?._netEvents.Dispose ();

if (IsWinPlatform) {
NetWinConsole?.Cleanup ();
Expand Down Expand Up @@ -611,7 +664,7 @@ public override void Init (Action terminalResized)

if (!RunningUnitTests) {
Console.TreatControlCAsInput = true;

Cols = Console.WindowWidth;
Rows = Console.WindowHeight;

Expand Down

0 comments on commit a3cb397

Please sign in to comment.