diff --git a/Terminal.Gui/ConsoleDrivers/AnsiEscapeSequenceRequest.cs b/Terminal.Gui/ConsoleDrivers/AnsiEscapeSequenceRequest.cs
index 3d4c9ebc3..4b9ecc833 100644
--- a/Terminal.Gui/ConsoleDrivers/AnsiEscapeSequenceRequest.cs
+++ b/Terminal.Gui/ConsoleDrivers/AnsiEscapeSequenceRequest.cs
@@ -52,7 +52,7 @@ public class AnsiEscapeSequenceRequest
/// Only call this method from the main UI thread. You should use if
/// sending many requests.
///
- public void Send () { Application.Driver?.RawWrite (Request); }
+ public void Send () { Application.Driver?.WriteRaw (Request); }
///
diff --git a/Terminal.Gui/ConsoleDrivers/AnsiResponseParser/AnsiRequestScheduler.cs b/Terminal.Gui/ConsoleDrivers/AnsiResponseParser/AnsiRequestScheduler.cs
index 12a2c270f..f7d6c4d9e 100644
--- a/Terminal.Gui/ConsoleDrivers/AnsiResponseParser/AnsiRequestScheduler.cs
+++ b/Terminal.Gui/ConsoleDrivers/AnsiResponseParser/AnsiRequestScheduler.cs
@@ -9,7 +9,7 @@ namespace Terminal.Gui;
/// to prevent console becoming unresponsive and handles evicting ignored requests (no reply from
/// terminal).
///
-internal class AnsiRequestScheduler
+public class AnsiRequestScheduler
{
private readonly IAnsiResponseParser _parser;
diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs
index 804cac02f..373cea16b 100644
--- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs
@@ -48,7 +48,7 @@ public abstract class ConsoleDriver : IConsoleDriver
///
/// How long after Esc has been pressed before we give up on getting an Ansi escape sequence
///
- internal TimeSpan EscTimeout = TimeSpan.FromMilliseconds (50);
+ public TimeSpan EscTimeout { get; } = TimeSpan.FromMilliseconds (50);
// As performance is a concern, we keep track of the dirty lines and only refresh those.
// This is in addition to the dirty flag on each cell.
@@ -710,16 +710,10 @@ public abstract class ConsoleDriver : IConsoleDriver
internal abstract IAnsiResponseParser GetParser ();
- internal AnsiRequestScheduler GetRequestScheduler ()
+ public AnsiRequestScheduler GetRequestScheduler ()
{
// Lazy initialization because GetParser is virtual
return _scheduler ??= new (GetParser ());
}
- ///
- /// Writes the given directly to the console (rather than the output
- /// draw buffer).
- ///
- ///
- internal abstract void RawWrite (string str);
}
\ No newline at end of file
diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
index 209e6e09e..cb2cb9140 100644
--- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
@@ -116,11 +116,6 @@ internal class CursesDriver : ConsoleDriver
}
}
- ///
- internal override void RawWrite (string str)
- {
- Console.Out.Write (str);
- }
public override void Suspend ()
{
diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
index aed22b6df..2648ee7e7 100644
--- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
@@ -400,9 +400,6 @@ public class FakeDriver : ConsoleDriver
///
internal override IAnsiResponseParser GetParser () => _parser;
- ///
- internal override void RawWrite (string str) { }
-
public void SetBufferSize (int width, int height)
{
FakeConsole.SetBufferSize (width, height);
diff --git a/Terminal.Gui/ConsoleDrivers/IConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/IConsoleDriver.cs
index d5e9cd009..99a6a916e 100644
--- a/Terminal.Gui/ConsoleDrivers/IConsoleDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/IConsoleDriver.cs
@@ -292,4 +292,17 @@ public interface IConsoleDriver
/// If simulates the Alt key being pressed.
/// If simulates the Ctrl key being pressed.
void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool ctrl);
+
+ ///
+ /// How long after Esc has been pressed before we give up on getting an Ansi escape sequence
+ ///
+ public TimeSpan EscTimeout { get; }
+
+ ///
+ /// Queues the given for execution
+ ///
+ ///
+ public void QueueAnsiRequest (AnsiEscapeSequenceRequest request);
+
+ public AnsiRequestScheduler GetRequestScheduler ();
}
diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver/NetDriver.cs
index dbcfb72f4..e0a94de6f 100644
--- a/Terminal.Gui/ConsoleDrivers/NetDriver/NetDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/NetDriver/NetDriver.cs
@@ -227,10 +227,6 @@ internal class NetDriver : ConsoleDriver
internal override IAnsiResponseParser GetParser () => _mainLoopDriver._netEvents.Parser;
internal NetMainLoop? _mainLoopDriver;
///
- internal override void RawWrite (string str)
- {
- Console.Write (str);
- }
diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver/NetEvents.cs b/Terminal.Gui/ConsoleDrivers/NetDriver/NetEvents.cs
index d7e0113cc..05af19e90 100644
--- a/Terminal.Gui/ConsoleDrivers/NetDriver/NetEvents.cs
+++ b/Terminal.Gui/ConsoleDrivers/NetDriver/NetEvents.cs
@@ -11,11 +11,11 @@ internal class NetEvents : IDisposable
//CancellationTokenSource _waitForStartCancellationTokenSource;
private readonly ManualResetEventSlim _winChange = new (false);
private readonly BlockingCollection _inputQueue = new (new ConcurrentQueue ());
- private readonly ConsoleDriver _consoleDriver;
+ private readonly IConsoleDriver _consoleDriver;
public AnsiResponseParser Parser { get; private set; } = new ();
- public NetEvents (ConsoleDriver consoleDriver)
+ public NetEvents (IConsoleDriver consoleDriver)
{
_consoleDriver = consoleDriver ?? throw new ArgumentNullException (nameof (consoleDriver));
diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsDriver.cs
index 251ad4dad..f5574b8a0 100644
--- a/Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver/WindowsDriver.cs
@@ -20,6 +20,7 @@ using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using static Terminal.Gui.ConsoleDrivers.ConsoleKeyMapping;
+using static Terminal.Gui.SpinnerStyle;
namespace Terminal.Gui;
@@ -187,7 +188,14 @@ internal class WindowsDriver : ConsoleDriver
}
}
- public override void WriteRaw (string ansi) { WinConsole?.WriteANSI (ansi); }
+ ///
+ internal override IAnsiResponseParser GetParser () => _parser;
+
+
+ public override void WriteRaw (string str)
+ {
+ WinConsole?.WriteANSI (str);
+ }
#region Not Implemented
@@ -217,11 +225,6 @@ internal class WindowsDriver : ConsoleDriver
public override void UpdateCursor ()
{
- if (RunningUnitTests)
- {
- return;
- }
-
if (Col < 0 || Row < 0 || Col >= Cols || Row >= Rows)
{
GetCursorVisibility (out CursorVisibility cursorVisibility);
@@ -480,14 +483,25 @@ internal class WindowsDriver : ConsoleDriver
if (!RunningUnitTests)
{
- WinConsole?.SetInitialCursorVisibility ();
+ WinConsole?.SetInitialCursorVisibility ();
}
return new MainLoop (_mainLoopDriver);
}
+ private AnsiResponseParser _parser = new ();
+
internal void ProcessInput (WindowsConsole.InputRecord inputEvent)
{
+ foreach (var e in Parse (inputEvent))
+ {
+ ProcessInputAfterParsing (e);
+ }
+ }
+
+ internal void ProcessInputAfterParsing (WindowsConsole.InputRecord inputEvent)
+ {
+
switch (inputEvent.EventType)
{
case WindowsConsole.EventType.Key:
@@ -510,22 +524,16 @@ internal class WindowsDriver : ConsoleDriver
break;
}
- if (inputEvent.KeyEvent.bKeyDown)
- {
- // Avoid sending repeat key down events
- OnKeyDown (new Key (map));
- }
- else
- {
- OnKeyUp (new Key (map));
- }
+ // This follows convention in NetDriver
+ OnKeyDown (new Key (map));
+ OnKeyUp (new Key (map));
break;
case WindowsConsole.EventType.Mouse:
MouseEventArgs me = ToDriverMouse (inputEvent.MouseEvent);
- if (me.Flags == MouseFlags.None)
+ if (me is null || me.Flags == MouseFlags.None)
{
break;
}
@@ -563,6 +571,43 @@ internal class WindowsDriver : ConsoleDriver
}
}
+ private IEnumerable Parse (WindowsConsole.InputRecord inputEvent)
+ {
+ if (inputEvent.EventType != WindowsConsole.EventType.Key)
+ {
+ yield return inputEvent;
+ yield break;
+ }
+
+ // Swallow key up events - they are unreliable
+ if (!inputEvent.KeyEvent.bKeyDown)
+ {
+ yield break;
+ }
+
+ foreach (var i in ShouldRelease ())
+ {
+ yield return i;
+ }
+
+ foreach (Tuple output in
+ _parser.ProcessInput (Tuple.Create (inputEvent.KeyEvent.UnicodeChar, inputEvent)))
+ {
+ yield return output.Item2;
+ }
+ }
+
+ public IEnumerable ShouldRelease ()
+ {
+ if (_parser.State == AnsiResponseParserState.ExpectingBracket &&
+ DateTime.Now - _parser.StateChangedAt > EscTimeout)
+ {
+ return _parser.Release ().Select (o => o.Item2);
+ }
+
+ return [];
+ }
+
#if HACK_CHECK_WINCHANGED
private void ChangeWin (object s, SizeChangedEventArgs e)
{
@@ -661,13 +706,13 @@ internal class WindowsDriver : ConsoleDriver
if (keyInfo.KeyChar == 0)
{
- // If the keyChar is 0, keyInfo.Key value is not a printable character.
+ // If the keyChar is 0, keyInfo.Key value is not a printable character.
- // Dead keys (diacritics) are indicated by setting the top bit of the return value.
+ // Dead keys (diacritics) are indicated by setting the top bit of the return value.
if ((mapResult & 0x80000000) != 0)
{
// Dead key (e.g. Oem2 '~'/'^' on POR keyboard)
- // Option 1: Throw it out.
+ // Option 1: Throw it out.
// - Apps will never see the dead keys
// - If user presses a key that can be combined with the dead key ('a'), the right thing happens (app will see '�').
// - NOTE: With Dead Keys, KeyDown != KeyUp. The KeyUp event will have just the base char ('a').
@@ -688,7 +733,7 @@ internal class WindowsDriver : ConsoleDriver
if (keyInfo.Modifiers != 0)
{
// These Oem keys have well-defined chars. We ensure the representative char is used.
- // If we don't do this, then on some keyboard layouts the wrong char is
+ // If we don't do this, then on some keyboard layouts the wrong char is
// returned (e.g. on ENG OemPlus un-shifted is =, not +). This is important
// for key persistence ("Ctrl++" vs. "Ctrl+=").
mappedChar = keyInfo.Key switch
@@ -754,11 +799,11 @@ internal class WindowsDriver : ConsoleDriver
if (keyInfo.KeyChar <= 'Z')
{
return (KeyCode)keyInfo.Key | KeyCode.ShiftMask;
- }
+ }
// Always return the KeyChar because it may be an Á, À with Oem1, etc
return (KeyCode)keyInfo.KeyChar;
- }
+ }
}
if (keyInfo.KeyChar <= 'z')
@@ -947,12 +992,12 @@ internal class WindowsDriver : ConsoleDriver
{
// TODO: This makes IConsoleDriver dependent on Application, which is not ideal. This should be moved to Application.
Application.MainLoop!.AddIdle (
- () =>
- {
- Task.Run (async () => await ProcessButtonDoubleClickedAsync ());
+ () =>
+ {
+ Task.Run (async () => await ProcessButtonDoubleClickedAsync ());
- return false;
- });
+ return false;
+ });
}
// The ButtonState member of the MouseEvent structure has bit corresponding to each mouse button.
@@ -1019,12 +1064,12 @@ internal class WindowsDriver : ConsoleDriver
{
// TODO: This makes IConsoleDriver dependent on Application, which is not ideal. This should be moved to Application.
Application.MainLoop!.AddIdle (
- () =>
- {
- Task.Run (async () => await ProcessContinuousButtonPressedAsync (mouseFlag));
+ () =>
+ {
+ Task.Run (async () => await ProcessContinuousButtonPressedAsync (mouseFlag));
- return false;
- });
+ return false;
+ });
}
}
else if (_lastMouseButtonPressed != null
@@ -1187,4 +1232,4 @@ internal class WindowsDriver : ConsoleDriver
Flags = mouseFlag
};
}
-}
+}
\ No newline at end of file
diff --git a/UICatalog/Scenarios/AnsiRequestsScenario.cs b/UICatalog/Scenarios/AnsiRequestsScenario.cs
index 54cd2411c..a0f711593 100644
--- a/UICatalog/Scenarios/AnsiRequestsScenario.cs
+++ b/UICatalog/Scenarios/AnsiRequestsScenario.cs
@@ -386,7 +386,7 @@ public sealed class AnsiEscapeSequenceRequests : Scenario
new ()
{
Request = EscSeqUtils.CSI_SendDeviceAttributes.Request,
- Terminator = EscSeqUtils.CSI_ReportDeviceAttributes_Terminator,
+ Terminator = EscSeqUtils.CSI_SendDeviceAttributes.Terminator,
ResponseReceived = HandleResponse
});
sends.Add (DateTime.Now);