From 27b15ec8ee0e9741547ddb78e264c5c784dd8b6f Mon Sep 17 00:00:00 2001 From: tznind Date: Wed, 27 Nov 2024 12:59:05 +0000 Subject: [PATCH] WIP Building after merge conflict resolved - but does it work? --- .../AnsiEscapeSequenceRequest.cs | 2 +- .../AnsiRequestScheduler.cs | 2 +- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 10 +- .../CursesDriver/CursesDriver.cs | 5 - .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 3 - Terminal.Gui/ConsoleDrivers/IConsoleDriver.cs | 13 ++ .../ConsoleDrivers/NetDriver/NetDriver.cs | 4 - .../ConsoleDrivers/NetDriver/NetEvents.cs | 4 +- .../WindowsDriver/WindowsDriver.cs | 113 ++++++++++++------ UICatalog/Scenarios/AnsiRequestsScenario.cs | 2 +- 10 files changed, 99 insertions(+), 59 deletions(-) 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);