Browse Source

Add WindowsDriverKeyPairer

tznind 9 months ago
parent
commit
d642fb6575

+ 28 - 17
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -20,6 +20,7 @@ using System.ComponentModel;
 using System.Diagnostics;
 using System.Runtime.InteropServices;
 using System.Text;
+using Terminal.Gui.ConsoleDrivers;
 using static Terminal.Gui.ConsoleDrivers.ConsoleKeyMapping;
 using static Terminal.Gui.SpinnerStyle;
 
@@ -1462,7 +1463,7 @@ internal class WindowsDriver : ConsoleDriver
     /// How long after Esc has been pressed before we give up on getting an Ansi escape sequence
     /// </summary>
     private TimeSpan _escTimeout = TimeSpan.FromMilliseconds (50);
-    public AnsiResponseParser<WindowsConsole.InputRecord> Parser { get; set; } = new ();
+    public AnsiResponseParser<WindowsConsole.InputRecord []> Parser { get; set; } = new ();
 
     internal void ProcessInput (WindowsConsole.InputRecord inputEvent)
     {
@@ -1553,6 +1554,7 @@ internal class WindowsDriver : ConsoleDriver
         }
     }
 
+    private WindowsDriverKeyPairer pairer = new WindowsDriverKeyPairer ();
     private IEnumerable<WindowsConsole.InputRecord> Parse (WindowsConsole.InputRecord inputEvent)
     {
         if (inputEvent.EventType != WindowsConsole.EventType.Key)
@@ -1561,26 +1563,36 @@ internal class WindowsDriver : ConsoleDriver
             yield break;
         }
 
-        // TODO: For now ignore key up events completely
-        if (!inputEvent.KeyEvent.bKeyDown)
-        {
-            yield break;
-        }
-
-        // TODO: Esc on its own is a problem - need a minute delay i.e. if you get Esc but nothing after release it.
-
-        // TODO: keydown/keyup badness
+        var pair = pairer.ProcessInput (inputEvent).ToArray ();
 
         foreach (var i in ShouldRelease ())
         {
             yield return i;
         }
 
-        foreach (Tuple<char, WindowsConsole.InputRecord> output in
-                 Parser.ProcessInput (Tuple.Create(inputEvent.KeyEvent.UnicodeChar,inputEvent)))
+        foreach (var p in pair)
         {
-            yield return output.Item2;
+            // may be down/up
+            if (p.Length == 2)
+            {
+                var c = p [0].KeyEvent.UnicodeChar;
+                foreach (Tuple<char, WindowsConsole.InputRecord []> output in
+                         Parser.ProcessInput (Tuple.Create(c,p)))
+                {
+                    foreach (var r in output.Item2)
+                    {
+                        yield return r;
+                    }
+                }
+            }
+            else
+            {
+                // environment doesn't support down/up
+
+                // TODO: what we do in this situation?
+            }
         }
+
     }
 
     public IEnumerable<WindowsConsole.InputRecord> ShouldRelease ()
@@ -1589,11 +1601,10 @@ internal class WindowsDriver : ConsoleDriver
         if (Parser.State == ParserState.ExpectingBracket &&
             DateTime.Now - Parser.StateChangedAt > _escTimeout)
         {
-            foreach (Tuple<char, WindowsConsole.InputRecord> output in Parser.Release ())
-            {
-                yield return output.Item2;
-            }
+            return Parser.Release ().SelectMany (o => o.Item2);
         }
+
+        return [];
     }
 
 #if HACK_CHECK_WINCHANGED

+ 74 - 0
Terminal.Gui/ConsoleDrivers/WindowsDriverKeyPairer.cs

@@ -0,0 +1,74 @@
+using static Terminal.Gui.WindowsConsole;
+
+namespace Terminal.Gui.ConsoleDrivers;
+class WindowsDriverKeyPairer
+{
+    private InputRecord? _heldDownEvent = null; // To hold the "down" event
+
+    // Process a single input record at a time
+    public IEnumerable<InputRecord []> ProcessInput (InputRecord record)
+    {
+        // If it's a "down" event, store it as a held event
+        if (IsKeyDown (record))
+        {
+            return HandleKeyDown (record);
+        }
+        // If it's an "up" event, try to match it with the held "down" event
+        else if (IsKeyUp (record))
+        {
+            return HandleKeyUp (record);
+        }
+        else
+        {
+            // If it's not a key event, just pass it through
+            return new [] { new [] { record } };
+        }
+    }
+
+    private IEnumerable<InputRecord []> HandleKeyDown (InputRecord record)
+    {
+        // If we already have a held "down" event, release it (unmatched)
+        if (_heldDownEvent != null)
+        {
+            // Release the previous "down" event since there's a new "down"
+            var previousDown = _heldDownEvent.Value;
+            _heldDownEvent = record; // Hold the new "down" event
+            return new [] { new [] { previousDown } };
+        }
+
+        // Hold the new "down" event
+        _heldDownEvent = record;
+        return Enumerable.Empty<InputRecord []> ();
+    }
+
+    private IEnumerable<InputRecord []> HandleKeyUp (InputRecord record)
+    {
+        // If we have a held "down" event that matches this "up" event, release both
+        if (_heldDownEvent != null && IsMatchingKey (record, _heldDownEvent.Value))
+        {
+            var downEvent = _heldDownEvent.Value;
+            _heldDownEvent = null; // Clear the held event
+            return new [] { new [] { downEvent, record } };
+        }
+        else
+        {
+            // No match, release the "up" event by itself
+            return new [] { new [] { record } };
+        }
+    }
+
+    private bool IsKeyDown (InputRecord record)
+    {
+        return record.KeyEvent.bKeyDown;
+    }
+
+    private bool IsKeyUp (InputRecord record)
+    {
+        return !record.KeyEvent.bKeyDown;
+    }
+
+    private bool IsMatchingKey (InputRecord upEvent, InputRecord downEvent)
+    {
+        return upEvent.KeyEvent.UnicodeChar == downEvent.KeyEvent.UnicodeChar;
+    }
+}