Browse Source

Improves a lot of console key mappings.

BDisp 9 months ago
parent
commit
1b6963f8db

+ 34 - 0
Terminal.Gui/ConsoleDrivers/ConsoleKeyMapping.cs

@@ -704,6 +704,32 @@ public static class ConsoleKeyMapping
                 return (uint)ConsoleKey.F24;
             case KeyCode.Tab | KeyCode.ShiftMask:
                 return (uint)ConsoleKey.Tab;
+            case KeyCode.Space:
+                return (uint)ConsoleKey.Spacebar;
+            default:
+                uint c = (char)keyValue;
+
+                if (c is >= (char)ConsoleKey.A and <= (char)ConsoleKey.Z)
+                {
+                    return c;
+                }
+
+                if ((c - 32) is >= (char)ConsoleKey.A and <= (char)ConsoleKey.Z)
+                {
+                    return (c - 32);
+                }
+
+                if (Enum.IsDefined (typeof (ConsoleKey), keyValue.ToString ()))
+                {
+                    return (uint)keyValue;
+                }
+
+                // DEL
+                if ((uint)keyValue == 127)
+                {
+                    return (uint)ConsoleKey.Backspace;
+                }
+                break;
         }
 
         isConsoleKey = false;
@@ -867,6 +893,14 @@ public static class ConsoleKeyMapping
             case ConsoleKey.Tab:
                 keyCode = KeyCode.Tab;
 
+                break;
+            case ConsoleKey.Spacebar:
+                keyCode = KeyCode.Space;
+
+                break;
+            case ConsoleKey.Backspace:
+                keyCode = KeyCode.Backspace;
+
                 break;
             default:
                 if ((int)consoleKeyInfo.KeyChar is >= 1 and <= 26)

+ 12 - 4
Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqReq.cs

@@ -29,14 +29,22 @@ public class EscSeqRequests
     ///     <see cref="EscSeqReqStatus"/> instance to <see cref="Statuses"/> list.
     /// </summary>
     /// <param name="ansiRequest">The <see cref="AnsiEscapeSequenceRequest"/> object.</param>
-    public void Add (AnsiEscapeSequenceRequest ansiRequest)
+    /// <param name="driver">The driver in use.</param>
+    public void Add (AnsiEscapeSequenceRequest ansiRequest, ConsoleDriver? driver = null)
     {
         lock (Statuses)
         {
             Statuses.Enqueue (new (ansiRequest));
-            Console.Out.Write (ansiRequest.Request);
-            Console.Out.Flush ();
-            Thread.Sleep (100); // Allow time for the terminal to respond
+
+            if (driver is null)
+            {
+                Console.Out.Write (ansiRequest.Request);
+                Console.Out.Flush ();
+            }
+            else
+            {
+                driver.WriteRaw (ansiRequest.Request);
+            }
         }
     }
 

+ 423 - 89
Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs

@@ -1,4 +1,6 @@
 #nullable enable
+using Terminal.Gui.ConsoleDrivers;
+
 namespace Terminal.Gui;
 
 /// <summary>
@@ -156,6 +158,11 @@ public static class EscSeqUtils
     /// <returns></returns>
     public static string CSI_ClearScreen (ClearScreenOptions option) { return $"{CSI}{(int)option}J"; }
 
+    /// <summary>
+    ///     Specify the incomplete <see cref="ConsoleKeyInfo"/> array not yet recognized as valid ANSI escape sequence.
+    /// </summary>
+    public static ConsoleKeyInfo []? IncompleteCkInfos { get; set; }
+
     /// <summary>
     ///     Decodes an ANSI escape sequence.
     /// </summary>
@@ -193,10 +200,10 @@ public static class EscSeqUtils
         char [] kChars = GetKeyCharArray (cki);
         (c1Control, code, values, terminator) = GetEscapeResult (kChars);
         isMouse = false;
-        buttonState = new List<MouseFlags> { 0 };
+        buttonState = [0];
         pos = default (Point);
         seqReqStatus = null;
-        char keyChar = '\0';
+        var keyChar = '\0';
 
         switch (c1Control)
         {
@@ -205,53 +212,112 @@ public static class EscSeqUtils
                 {
                     key = ConsoleKey.Escape;
 
-                    newConsoleKeyInfo = new ConsoleKeyInfo (
-                                                            cki [0].KeyChar,
-                                                            key,
-                                                            (mod & ConsoleModifiers.Shift) != 0,
-                                                            (mod & ConsoleModifiers.Alt) != 0,
-                                                            (mod & ConsoleModifiers.Control) != 0);
+                    newConsoleKeyInfo = new (
+                                             cki [0].KeyChar,
+                                             key,
+                                             (mod & ConsoleModifiers.Shift) != 0,
+                                             (mod & ConsoleModifiers.Alt) != 0,
+                                             (mod & ConsoleModifiers.Control) != 0);
                 }
-                else if ((uint)cki [1].KeyChar >= 1 && (uint)cki [1].KeyChar <= 26)
+                else if ((uint)cki [1].KeyChar >= 1 && (uint)cki [1].KeyChar <= 26 && (uint)cki [1].KeyChar != '\n' && (uint)cki [1].KeyChar != '\r')
                 {
                     key = (ConsoleKey)(char)(cki [1].KeyChar + (uint)ConsoleKey.A - 1);
+                    mod = ConsoleModifiers.Alt | ConsoleModifiers.Control;
+
+                    newConsoleKeyInfo = new (
+                                             cki [1].KeyChar,
+                                             key,
+                                             (mod & ConsoleModifiers.Shift) != 0,
+                                             (mod & ConsoleModifiers.Alt) != 0,
+                                             (mod & ConsoleModifiers.Control) != 0);
+                }
+                else if (cki [1].KeyChar >= 65 && cki [1].KeyChar <= 90)
+                {
+                    key = (ConsoleKey)cki [1].KeyChar;
+                    mod = ConsoleModifiers.Shift | ConsoleModifiers.Alt;
+
+                    newConsoleKeyInfo = new (
+                                             cki [1].KeyChar,
+                                             (ConsoleKey)Math.Min ((uint)key, 255),
+                                             (mod & ConsoleModifiers.Shift) != 0,
+                                             (mod & ConsoleModifiers.Alt) != 0,
+                                             (mod & ConsoleModifiers.Control) != 0);
+                }
+                else if (cki [1].KeyChar >= 97 && cki [1].KeyChar <= 122)
+                {
+                    key = (ConsoleKey)cki [1].KeyChar.ToString ().ToUpper () [0];
+                    mod = ConsoleModifiers.Alt;
+
+                    newConsoleKeyInfo = new (
+                                             cki [1].KeyChar,
+                                             (ConsoleKey)Math.Min ((uint)key, 255),
+                                             (mod & ConsoleModifiers.Shift) != 0,
+                                             (mod & ConsoleModifiers.Alt) != 0,
+                                             (mod & ConsoleModifiers.Control) != 0);
+                }
+                else if (cki [1].KeyChar is '\0' or ' ')
+                {
+                    key = ConsoleKey.Spacebar;
 
-                    newConsoleKeyInfo = new ConsoleKeyInfo (
-                                                            cki [1].KeyChar,
-                                                            key,
-                                                            false,
-                                                            true,
-                                                            true);
+                    if (kChars.Length > 1 && kChars [1] == '\0')
+                    {
+                        mod = ConsoleModifiers.Alt | ConsoleModifiers.Control;
+                    }
+                    else
+                    {
+                        mod = ConsoleModifiers.Shift | ConsoleModifiers.Alt;
+                    }
+
+                    newConsoleKeyInfo = new (
+                                             cki [1].KeyChar,
+                                             (ConsoleKey)Math.Min ((uint)key, 255),
+                                             (mod & ConsoleModifiers.Shift) != 0,
+                                             (mod & ConsoleModifiers.Alt) != 0,
+                                             (mod & ConsoleModifiers.Control) != 0);
                 }
-                else
+                else if (cki [1].KeyChar is '\n' or '\r')
                 {
-                    if (cki [1].KeyChar >= 97 && cki [1].KeyChar <= 122)
+                    key = ConsoleKey.Enter;
+
+                    if (kChars.Length > 1 && kChars [1] == '\n')
                     {
-                        key = (ConsoleKey)cki [1].KeyChar.ToString ().ToUpper () [0];
+                        mod = ConsoleModifiers.Alt | ConsoleModifiers.Control;
                     }
                     else
                     {
-                        key = (ConsoleKey)cki [1].KeyChar;
+                        mod = ConsoleModifiers.Shift | ConsoleModifiers.Alt;
                     }
 
-                    newConsoleKeyInfo = new ConsoleKeyInfo (
-                                                            (char)key,
-                                                            (ConsoleKey)Math.Min ((uint)key, 255),
-                                                            false,
-                                                            true,
-                                                            false);
+                    newConsoleKeyInfo = new (
+                                             cki [1].KeyChar,
+                                             (ConsoleKey)Math.Min ((uint)key, 255),
+                                             (mod & ConsoleModifiers.Shift) != 0,
+                                             (mod & ConsoleModifiers.Alt) != 0,
+                                             (mod & ConsoleModifiers.Control) != 0);
+                }
+                else
+                {
+                    key = (ConsoleKey)cki [1].KeyChar;
+                    mod = ConsoleModifiers.Alt;
+
+                    newConsoleKeyInfo = new (
+                                             cki [1].KeyChar,
+                                             (ConsoleKey)Math.Min ((uint)key, 255),
+                                             (mod & ConsoleModifiers.Shift) != 0,
+                                             (mod & ConsoleModifiers.Alt) != 0,
+                                             (mod & ConsoleModifiers.Control) != 0);
                 }
 
                 break;
             case "SS3":
                 key = GetConsoleKey (terminator [0], values [0], ref mod, ref keyChar);
 
-                newConsoleKeyInfo = new ConsoleKeyInfo (
-                                                        keyChar,
-                                                        key,
-                                                        (mod & ConsoleModifiers.Shift) != 0,
-                                                        (mod & ConsoleModifiers.Alt) != 0,
-                                                        (mod & ConsoleModifiers.Control) != 0);
+                newConsoleKeyInfo = new (
+                                         keyChar,
+                                         key,
+                                         (mod & ConsoleModifiers.Shift) != 0,
+                                         (mod & ConsoleModifiers.Alt) != 0,
+                                         (mod & ConsoleModifiers.Control) != 0);
 
                 break;
             case "CSI":
@@ -279,31 +345,32 @@ public static class EscSeqUtils
                         mod |= GetConsoleModifiers (values [1]);
                     }
 
-                    newConsoleKeyInfo = new ConsoleKeyInfo (
-                                                            keyChar,
-                                                            key,
-                                                            (mod & ConsoleModifiers.Shift) != 0,
-                                                            (mod & ConsoleModifiers.Alt) != 0,
-                                                            (mod & ConsoleModifiers.Control) != 0);
+                    newConsoleKeyInfo = new (
+                                             keyChar,
+                                             key,
+                                             (mod & ConsoleModifiers.Shift) != 0,
+                                             (mod & ConsoleModifiers.Alt) != 0,
+                                             (mod & ConsoleModifiers.Control) != 0);
                 }
                 else
                 {
                     // BUGBUG: See https://github.com/gui-cs/Terminal.Gui/issues/2803
                     // This is caused by NetDriver depending on Console.KeyAvailable?
-                    throw new InvalidOperationException ("CSI response, but there's no terminator");
+                    //throw new InvalidOperationException ("CSI response, but there's no terminator");
 
-                    //newConsoleKeyInfo = new ConsoleKeyInfo ('\0',
-                    //	key,
-                    //	(mod & ConsoleModifiers.Shift) != 0,
-                    //	(mod & ConsoleModifiers.Alt) != 0,
-                    //	(mod & ConsoleModifiers.Control) != 0);
+                    IncompleteCkInfos = cki;
                 }
 
+                break;
+            default:
+                newConsoleKeyInfo = MapConsoleKeyInfo (cki [0]);
+                key = newConsoleKeyInfo.Key;
+                mod = newConsoleKeyInfo.Modifiers;
+
                 break;
         }
     }
 
-    #nullable enable
     /// <summary>
     ///     Gets the c1Control used in the called escape sequence.
     /// </summary>
@@ -369,6 +436,7 @@ public static class EscSeqUtils
                    ('B', _) => ConsoleKey.DownArrow,
                    ('C', _) => ConsoleKey.RightArrow,
                    ('D', _) => ConsoleKey.LeftArrow,
+                   ('E', _) => ConsoleKey.Clear,
                    ('F', _) => ConsoleKey.End,
                    ('H', _) => ConsoleKey.Home,
                    ('P', _) => ConsoleKey.F1,
@@ -388,18 +456,19 @@ public static class EscSeqUtils
                    ('~', "21") => ConsoleKey.F10,
                    ('~', "23") => ConsoleKey.F11,
                    ('~', "24") => ConsoleKey.F12,
-                   ('l', _) => ConsoleKey.Add,
-                   ('m', _) => ConsoleKey.Subtract,
-                   ('p', _) => ConsoleKey.Insert,
-                   ('q', _) => ConsoleKey.End,
-                   ('r', _) => ConsoleKey.DownArrow,
-                   ('s', _) => ConsoleKey.PageDown,
-                   ('t', _) => ConsoleKey.LeftArrow,
-                   ('u', _) => ConsoleKey.Clear,
-                   ('v', _) => ConsoleKey.RightArrow,
-                   ('w', _) => ConsoleKey.Home,
-                   ('x', _) => ConsoleKey.UpArrow,
-                   ('y', _) => ConsoleKey.PageUp,
+                   // These terminators are used by macOS on a numeric keypad without keys modifiers
+                   ('l', null) => ConsoleKey.Add,
+                   ('m', null) => ConsoleKey.Subtract,
+                   ('p', null) => ConsoleKey.Insert,
+                   ('q', null) => ConsoleKey.End,
+                   ('r', null) => ConsoleKey.DownArrow,
+                   ('s', null) => ConsoleKey.PageDown,
+                   ('t', null) => ConsoleKey.LeftArrow,
+                   ('u', null) => ConsoleKey.Clear,
+                   ('v', null) => ConsoleKey.RightArrow,
+                   ('w', null) => ConsoleKey.Home,
+                   ('x', null) => ConsoleKey.UpArrow,
+                   ('y', null) => ConsoleKey.PageUp,
                    (_, _) => 0
                };
     }
@@ -434,7 +503,7 @@ public static class EscSeqUtils
     /// </returns>
     public static (string c1Control, string code, string [] values, string terminating) GetEscapeResult (char [] kChar)
     {
-        if (kChar is null || kChar.Length == 0)
+        if (kChar is null || kChar.Length == 0 || (kChar.Length == 1 && kChar [0] != KeyEsc))
         {
             return (null, null, null, null);
         }
@@ -803,7 +872,7 @@ public static class EscSeqUtils
 
             if ((mouseFlags [0] & MouseFlags.ReportMousePosition) == 0)
             {
-                Application.MainLoop.AddIdle (
+                Application.MainLoop?.AddIdle (
                                               () =>
                                               {
                                                   // INTENT: What's this trying to do?
@@ -846,7 +915,7 @@ public static class EscSeqUtils
             _isButtonClicked = false;
             _isButtonDoubleClicked = true;
 
-            Application.MainLoop.AddIdle (
+            Application.MainLoop?.AddIdle (
                                           () =>
                                           {
                                               Task.Run (async () => await ProcessButtonDoubleClickedAsync ());
@@ -885,7 +954,7 @@ public static class EscSeqUtils
                 mouseFlags.Add (GetButtonClicked (buttonState));
                 _isButtonClicked = true;
 
-                Application.MainLoop.AddIdle (
+                Application.MainLoop?.AddIdle (
                                               () =>
                                               {
                                                   Task.Run (async () => await ProcessButtonClickedAsync ());
@@ -952,7 +1021,7 @@ public static class EscSeqUtils
     public static ConsoleKeyInfo MapConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
     {
         ConsoleKeyInfo newConsoleKeyInfo = consoleKeyInfo;
-        ConsoleKey key;
+        ConsoleKey key = ConsoleKey.None;
         char keyChar = consoleKeyInfo.KeyChar;
 
         switch ((uint)keyChar)
@@ -960,56 +1029,165 @@ public static class EscSeqUtils
             case 0:
                 if (consoleKeyInfo.Key == (ConsoleKey)64)
                 { // Ctrl+Space in Windows.
-                    newConsoleKeyInfo = new ConsoleKeyInfo (
-                                                            ' ',
-                                                            ConsoleKey.Spacebar,
-                                                            (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
-                                                            (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
-                                                            (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0);
+                    newConsoleKeyInfo = new (
+                                             consoleKeyInfo.KeyChar,
+                                             ConsoleKey.Spacebar,
+                                             (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
+                                             (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
+                                             (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0);
+                }
+                else if (consoleKeyInfo.Key == ConsoleKey.None)
+                {
+                    newConsoleKeyInfo = new (
+                                             consoleKeyInfo.KeyChar,
+                                             ConsoleKey.Spacebar,
+                                             (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
+                                             (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
+                                             true);
                 }
 
                 break;
-            case uint n when n > 0 && n <= KeyEsc:
-                if (consoleKeyInfo.Key == 0 && consoleKeyInfo.KeyChar == '\r')
+            case uint n when n is > 0 and <= KeyEsc:
+                if (consoleKeyInfo is { Key: 0, KeyChar: '\t' })
+                {
+                    key = ConsoleKey.Tab;
+
+                    newConsoleKeyInfo = new (
+                                             consoleKeyInfo.KeyChar,
+                                             key,
+                                             (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
+                                             (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
+                                             (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0);
+                }
+                else if (consoleKeyInfo is { Key: 0, KeyChar: '\r' })
                 {
                     key = ConsoleKey.Enter;
 
-                    newConsoleKeyInfo = new ConsoleKeyInfo (
-                                                            consoleKeyInfo.KeyChar,
-                                                            key,
-                                                            (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
-                                                            (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
-                                                            (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0);
+                    newConsoleKeyInfo = new (
+                                             consoleKeyInfo.KeyChar,
+                                             key,
+                                             (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
+                                             (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
+                                             (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0);
+                }
+                else if (consoleKeyInfo is { Key: 0, KeyChar: '\n' })
+                {
+                    key = ConsoleKey.Enter;
+
+                    newConsoleKeyInfo = new (
+                                             consoleKeyInfo.KeyChar,
+                                             key,
+                                             (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
+                                             (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
+                                             true);
                 }
                 else if (consoleKeyInfo.Key == 0)
                 {
                     key = (ConsoleKey)(char)(consoleKeyInfo.KeyChar + (uint)ConsoleKey.A - 1);
 
-                    newConsoleKeyInfo = new ConsoleKeyInfo (
-                                                            (char)key,
-                                                            key,
-                                                            (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
-                                                            (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
-                                                            true);
+                    newConsoleKeyInfo = new (
+                                             consoleKeyInfo.KeyChar,
+                                             key,
+                                             (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
+                                             (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
+                                             true);
                 }
 
                 break;
             case 127: // DEL
-                newConsoleKeyInfo = new ConsoleKeyInfo (
-                                                        consoleKeyInfo.KeyChar,
-                                                        ConsoleKey.Backspace,
-                                                        (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
-                                                        (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
-                                                        (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0);
+                key = ConsoleKey.Backspace;
+
+                newConsoleKeyInfo = new (
+                                         consoleKeyInfo.KeyChar,
+                                         key,
+                                         (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
+                                         (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
+                                         (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0);
 
                 break;
             default:
-                newConsoleKeyInfo = consoleKeyInfo;
+                uint ck = ConsoleKeyMapping.MapKeyCodeToConsoleKey ((KeyCode)consoleKeyInfo.KeyChar, out bool isConsoleKey);
+
+                if (isConsoleKey)
+                {
+                    key = (ConsoleKey)ck;
+                }
+
+                newConsoleKeyInfo = new (
+                                         consoleKeyInfo.KeyChar,
+                                         key,
+                                         GetShiftMod (consoleKeyInfo.Modifiers),
+                                         (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
+                                         (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0);
 
                 break;
         }
 
         return newConsoleKeyInfo;
+
+        bool GetShiftMod (ConsoleModifiers modifiers)
+        {
+            if (consoleKeyInfo.KeyChar is >= (char)ConsoleKey.A and <= (char)ConsoleKey.Z && modifiers == ConsoleModifiers.None)
+            {
+                return true;
+            }
+
+            return (modifiers & ConsoleModifiers.Shift) != 0;
+        }
+    }
+
+    private static MouseFlags _lastMouseFlags;
+
+    /// <summary>
+    ///     Provides a handler to be invoked when mouse continuous button pressed is processed.
+    /// </summary>
+    public static event EventHandler<MouseEventArgs> ContinuousButtonPressed;
+
+    /// <summary>
+    ///     Provides a default mouse event handler that can be used by any driver.
+    /// </summary>
+    /// <param name="mouseFlag">The mouse flags event.</param>
+    /// <param name="pos">The mouse position.</param>
+    public static void ProcessMouseEvent (MouseFlags mouseFlag, Point pos)
+    {
+        bool WasButtonReleased (MouseFlags flag)
+        {
+            return flag.HasFlag (MouseFlags.Button1Released)
+                   || flag.HasFlag (MouseFlags.Button2Released)
+                   || flag.HasFlag (MouseFlags.Button3Released)
+                   || flag.HasFlag (MouseFlags.Button4Released);
+        }
+
+        bool IsButtonNotPressed (MouseFlags flag)
+        {
+            return !flag.HasFlag (MouseFlags.Button1Pressed)
+                   && !flag.HasFlag (MouseFlags.Button2Pressed)
+                   && !flag.HasFlag (MouseFlags.Button3Pressed)
+                   && !flag.HasFlag (MouseFlags.Button4Pressed);
+        }
+
+        bool IsButtonClickedOrDoubleClicked (MouseFlags flag)
+        {
+            return flag.HasFlag (MouseFlags.Button1Clicked)
+                   || flag.HasFlag (MouseFlags.Button2Clicked)
+                   || flag.HasFlag (MouseFlags.Button3Clicked)
+                   || flag.HasFlag (MouseFlags.Button4Clicked)
+                   || flag.HasFlag (MouseFlags.Button1DoubleClicked)
+                   || flag.HasFlag (MouseFlags.Button2DoubleClicked)
+                   || flag.HasFlag (MouseFlags.Button3DoubleClicked)
+                   || flag.HasFlag (MouseFlags.Button4DoubleClicked);
+        }
+
+        if ((WasButtonReleased (mouseFlag) && IsButtonNotPressed (_lastMouseFlags)) || (IsButtonClickedOrDoubleClicked (mouseFlag) && _lastMouseFlags == 0))
+        {
+            return;
+        }
+
+        _lastMouseFlags = mouseFlag;
+
+        var me = new MouseEventArgs { Flags = mouseFlag, Position = pos };
+
+        ContinuousButtonPressed?.Invoke ((mouseFlag, pos), me);
     }
 
     /// <summary>
@@ -1021,7 +1199,61 @@ public static class EscSeqUtils
     public static ConsoleKeyInfo [] ResizeArray (ConsoleKeyInfo consoleKeyInfo, ConsoleKeyInfo [] cki)
     {
         Array.Resize (ref cki, cki is null ? 1 : cki.Length + 1);
-        cki [cki.Length - 1] = consoleKeyInfo;
+        cki [^1] = consoleKeyInfo;
+
+        return cki;
+    }
+
+    /// <summary>
+    ///     Insert a <see cref="ConsoleKeyInfo"/> array into the another <see cref="ConsoleKeyInfo"/> array at the specified
+    ///     index.
+    /// </summary>
+    /// <param name="toInsert">The array to insert.</param>
+    /// <param name="cki">The array where will be added the array.</param>
+    /// <param name="index">The start index to insert the array, default is 0.</param>
+    /// <returns>The <see cref="ConsoleKeyInfo"/> array with another array inserted.</returns>
+    public static ConsoleKeyInfo [] InsertArray ([CanBeNull] ConsoleKeyInfo [] toInsert, ConsoleKeyInfo [] cki, int index = 0)
+    {
+        if (toInsert is null)
+        {
+            return cki;
+        }
+
+        if (cki is null)
+        {
+            return toInsert;
+        }
+
+        if (index < 0)
+        {
+            index = 0;
+        }
+
+        ConsoleKeyInfo [] backupCki = cki.Clone () as ConsoleKeyInfo [];
+
+        Array.Resize (ref cki, cki.Length + toInsert.Length);
+
+        for (var i = 0; i < cki.Length; i++)
+        {
+            if (i == index)
+            {
+                for (var j = 0; j < toInsert.Length; j++)
+                {
+                    cki [i] = toInsert [j];
+                    i++;
+                }
+
+                for (int k = index; k < backupCki!.Length; k++)
+                {
+                    cki [i] = backupCki [k];
+                    i++;
+                }
+            }
+            else
+            {
+                cki [i] = backupCki! [i];
+            }
+        }
 
         return cki;
     }
@@ -1156,6 +1388,108 @@ public static class EscSeqUtils
         return mouseFlag;
     }
 
+    /// <summary>
+    ///     Split a raw string into a list of string with the correct ansi escape sequence.
+    /// </summary>
+    /// <param name="rawData">The raw string containing one or many ansi escape sequence.</param>
+    /// <returns>A list with a valid ansi escape sequence.</returns>
+    public static List<string> SplitEscapeRawString (string rawData)
+    {
+        List<string> splitList = [];
+        var isEscSeq = false;
+        var split = string.Empty;
+        char previousChar = '\0';
+
+        for (var i = 0; i < rawData.Length; i++)
+        {
+            char c = rawData [i];
+
+            if (c == '\u001B')
+            {
+                isEscSeq = true;
+
+                split = AddAndClearSplit ();
+
+                split += c.ToString ();
+            }
+            else if (!isEscSeq && c >= Key.Space)
+            {
+                split = AddAndClearSplit ();
+                splitList.Add (c.ToString ());
+            }
+            else if (previousChar != '\u001B' && c < Key.Space)// uint n when n is > 0 and <= KeyEsc
+            {
+                isEscSeq = false;
+                split = AddAndClearSplit ();
+                splitList.Add (c.ToString ());
+            }
+            else
+            {
+                split += c.ToString ();
+            }
+
+            if (!string.IsNullOrEmpty (split) && i == rawData.Length - 1)
+            {
+                splitList.Add (split);
+            }
+
+            previousChar = c;
+        }
+
+        return splitList;
+
+        string AddAndClearSplit ()
+        {
+            if (!string.IsNullOrEmpty (split))
+            {
+                splitList.Add (split);
+                split = string.Empty;
+            }
+
+            return split;
+        }
+    }
+
+    /// <summary>
+    ///     Convert a <see cref="ConsoleKeyInfo"/> array to string.
+    /// </summary>
+    /// <param name="consoleKeyInfos"></param>
+    /// <returns>The string representing the array.</returns>
+    public static string ToString (ConsoleKeyInfo [] consoleKeyInfos)
+    {
+        StringBuilder sb = new ();
+
+        foreach (ConsoleKeyInfo keyChar in consoleKeyInfos)
+        {
+            sb.Append (keyChar.KeyChar);
+        }
+
+        return sb.ToString ();
+    }
+
+    /// <summary>
+    /// Convert a string to <see cref="ConsoleKeyInfo"/> array.
+    /// </summary>
+    /// <param name="ansi"></param>
+    /// <returns>The <see cref="ConsoleKeyInfo"/>representing the string.</returns>
+    public static ConsoleKeyInfo [] ToConsoleKeyInfoArray (string ansi)
+    {
+        if (ansi is null)
+        {
+            return null;
+        }
+
+        ConsoleKeyInfo [] cki = new ConsoleKeyInfo [ansi.Length];
+
+        for (var i = 0; i < ansi.Length; i++)
+        {
+            char c = ansi [i];
+            cki [i] = new (c, 0, false, false, false);
+        }
+
+        return cki;
+    }
+
     #region Cursor
 
     //ESC [ M - RI Reverse Index – Performs the reverse operation of \n, moves cursor up one line, maintains horizontal position, scrolls buffer if necessary*

+ 375 - 28
UnitTests/Input/EscSeqUtilsTests.cs

@@ -23,7 +23,7 @@ public class EscSeqUtilsTests
 
     [Fact]
     [AutoInitShutdown]
-    public void DecodeEscSeq_Tests ()
+    public void DecodeEscSeq_Multiple_Tests ()
     {
         // ESC
         _cki = new ConsoleKeyInfo [] { new ('\u001b', 0, false, false, false) };
@@ -83,7 +83,7 @@ public class EscSeqUtilsTests
         Assert.Null (_escSeqReqProc);
         Assert.Equal (expectedCki, _newConsoleKeyInfo);
         Assert.Equal (ConsoleKey.R, _key);
-        Assert.Equal (0, (int)_mod);
+        Assert.Equal (ConsoleModifiers.Alt | ConsoleModifiers.Control, _mod);
         Assert.Equal ("ESC", _c1Control);
         Assert.Null (_code);
         Assert.Null (_values);
@@ -97,7 +97,7 @@ public class EscSeqUtilsTests
 
         ClearAll ();
         _cki = new ConsoleKeyInfo [] { new ('\u001b', 0, false, false, false), new ('r', 0, false, false, false) };
-        expectedCki = new ('R', ConsoleKey.R, false, true, false);
+        expectedCki = new ('r', ConsoleKey.R, false, true, false);
 
         EscSeqUtils.DecodeEscSeq (
                                   _escSeqReqProc,
@@ -118,7 +118,7 @@ public class EscSeqUtilsTests
         Assert.Null (_escSeqReqProc);
         Assert.Equal (expectedCki, _newConsoleKeyInfo);
         Assert.Equal (ConsoleKey.R, _key);
-        Assert.Equal (0, (int)_mod);
+        Assert.Equal (ConsoleModifiers.Alt, _mod);
         Assert.Equal ("ESC", _c1Control);
         Assert.Null (_code);
         Assert.Null (_values);
@@ -877,6 +877,260 @@ public class EscSeqUtilsTests
         Assert.NotNull (_seqReqStatus);
         Assert.Equal (0, (int)_arg1);
         Assert.Equal (Point.Empty, _arg2);
+
+        ClearAll ();
+    }
+
+    [Theory]
+    [InlineData ('A', ConsoleKey.A, true, true, false, "ESC", '\u001b', 'A')]
+    [InlineData ('a', ConsoleKey.A, false, true, false, "ESC", '\u001b', 'a')]
+    [InlineData ('\0', ConsoleKey.Spacebar, false, true, true, "ESC", '\u001b', '\0')]
+    [InlineData (' ', ConsoleKey.Spacebar, true, true, false, "ESC", '\u001b', ' ')]
+    [InlineData ('\n', ConsoleKey.Enter, false, true, true, "ESC", '\u001b', '\n')]
+    [InlineData ('\r', ConsoleKey.Enter, true, true, false, "ESC", '\u001b', '\r')]
+    public void DecodeEscSeq_More_Multiple_Tests (
+        char keyChar,
+        ConsoleKey consoleKey,
+        bool shift,
+        bool alt,
+        bool control,
+        string c1Control,
+        params char [] kChars
+    )
+    {
+        _cki = new ConsoleKeyInfo [kChars.Length];
+
+        for (var i = 0; i < kChars.Length; i++)
+        {
+            char kChar = kChars [i];
+            _cki [i] = new (kChar, 0, false, false, false);
+        }
+
+        var expectedCki = new ConsoleKeyInfo (keyChar, consoleKey, shift, alt, control);
+
+        EscSeqUtils.DecodeEscSeq (
+                                  _escSeqReqProc,
+                                  ref _newConsoleKeyInfo,
+                                  ref _key,
+                                  _cki,
+                                  ref _mod,
+                                  out _c1Control,
+                                  out _code,
+                                  out _values,
+                                  out _terminating,
+                                  out _isKeyMouse,
+                                  out _mouseFlags,
+                                  out _pos,
+                                  out _seqReqStatus,
+                                  ProcessContinuousButtonPressed
+                                 );
+        Assert.Null (_escSeqReqProc);
+        Assert.Equal (expectedCki, _newConsoleKeyInfo);
+        Assert.Equal (consoleKey, _key);
+
+        ConsoleModifiers mods = new ();
+
+        if (shift)
+        {
+            mods = ConsoleModifiers.Shift;
+        }
+
+        if (alt)
+        {
+            mods |= ConsoleModifiers.Alt;
+        }
+
+        if (control)
+        {
+            mods |= ConsoleModifiers.Control;
+        }
+
+        Assert.Equal (mods, _mod);
+        Assert.Equal (c1Control, _c1Control);
+        Assert.Null (_code);
+        Assert.Null (_values);
+        Assert.Equal (keyChar.ToString (), _terminating);
+        Assert.False (_isKeyMouse);
+        Assert.Equal (new () { 0 }, _mouseFlags);
+        Assert.Equal (Point.Empty, _pos);
+        Assert.Null (_seqReqStatus);
+        Assert.Equal (0, (int)_arg1);
+        Assert.Equal (Point.Empty, _arg2);
+
+        ClearAll ();
+    }
+
+    [Fact]
+    public void DecodeEscSeq_IncompleteCKInfos ()
+    {
+        // This is simulated response from a CSI_ReportTerminalSizeInChars
+        _cki =
+        [
+            new ('\u001b', 0, false, false, false),
+            new ('[', 0, false, false, false),
+            new ('8', 0, false, false, false),
+            new (';', 0, false, false, false),
+            new ('1', 0, false, false, false),
+        ];
+
+        ConsoleKeyInfo expectedCki = default;
+
+        EscSeqUtils.DecodeEscSeq (
+                                  _escSeqReqProc,
+                                  ref _newConsoleKeyInfo,
+                                  ref _key,
+                                  _cki,
+                                  ref _mod,
+                                  out _c1Control,
+                                  out _code,
+                                  out _values,
+                                  out _terminating,
+                                  out _isKeyMouse,
+                                  out _mouseFlags,
+                                  out _pos,
+                                  out _seqReqStatus,
+                                  ProcessContinuousButtonPressed
+                                 );
+        Assert.Null (_escSeqReqProc);
+        Assert.Equal (expectedCki, _newConsoleKeyInfo);
+        Assert.Equal (ConsoleKey.None, _key);
+        Assert.Equal (ConsoleModifiers.None, _mod);
+        Assert.Equal ("CSI", _c1Control);
+        Assert.Null (_code);
+        Assert.Equal (2, _values.Length);
+        Assert.Equal ("", _terminating);
+        Assert.False (_isKeyMouse);
+        Assert.Equal ([0], _mouseFlags);
+        Assert.Equal (Point.Empty, _pos);
+        Assert.Null (_seqReqStatus);
+        Assert.Equal (0, (int)_arg1);
+        Assert.Equal (Point.Empty, _arg2);
+        Assert.Equal (_cki, EscSeqUtils.IncompleteCkInfos);
+
+        _cki = EscSeqUtils.InsertArray (
+                                        EscSeqUtils.IncompleteCkInfos,
+                                        [
+                                            new ('0', 0, false, false, false),
+                                            new (';', 0, false, false, false),
+                                            new ('2', 0, false, false, false),
+                                            new ('0', 0, false, false, false),
+                                            new ('t', 0, false, false, false)
+                                        ]);
+
+        expectedCki = default;
+
+        EscSeqUtils.DecodeEscSeq (
+                                  _escSeqReqProc,
+                                  ref _newConsoleKeyInfo,
+                                  ref _key,
+                                  _cki,
+                                  ref _mod,
+                                  out _c1Control,
+                                  out _code,
+                                  out _values,
+                                  out _terminating,
+                                  out _isKeyMouse,
+                                  out _mouseFlags,
+                                  out _pos,
+                                  out _seqReqStatus,
+                                  ProcessContinuousButtonPressed
+                                 );
+        Assert.Null (_escSeqReqProc);
+        Assert.Equal (expectedCki, _newConsoleKeyInfo);
+        Assert.Equal (ConsoleKey.None, _key);
+
+        Assert.Equal (ConsoleModifiers.None, _mod);
+        Assert.Equal ("CSI", _c1Control);
+        Assert.Null (_code);
+        Assert.Equal (3, _values.Length);
+        Assert.Equal ("t", _terminating);
+        Assert.False (_isKeyMouse);
+        Assert.Equal ([0], _mouseFlags);
+        Assert.Equal (Point.Empty, _pos);
+        Assert.Null (_seqReqStatus);
+        Assert.Equal (0, (int)_arg1);
+        Assert.Equal (Point.Empty, _arg2);
+        Assert.NotEqual (_cki, EscSeqUtils.IncompleteCkInfos);
+        Assert.Contains (EscSeqUtils.ToString (EscSeqUtils.IncompleteCkInfos), EscSeqUtils.ToString (_cki));
+
+        ClearAll ();
+    }
+
+    [Theory]
+    [InlineData ('\u001B', ConsoleKey.Escape, false, false, false)]
+    [InlineData ('\r', ConsoleKey.Enter, false, false, false)]
+    [InlineData ('1', ConsoleKey.D1, false, false, false)]
+    [InlineData ('!', ConsoleKey.None, false, false, false)]
+    [InlineData ('a', ConsoleKey.A, false, false, false)]
+    [InlineData ('A', ConsoleKey.A, true, false, false)]
+    [InlineData ('\u0001', ConsoleKey.A, false, false, true)]
+    [InlineData ('\0', ConsoleKey.Spacebar, false, false, true)]
+    [InlineData ('\n', ConsoleKey.Enter, false, false, true)]
+    [InlineData ('\t', ConsoleKey.Tab, false, false, false)]
+    public void DecodeEscSeq_Single_Tests (char keyChar, ConsoleKey consoleKey, bool shift, bool alt, bool control)
+    {
+        _cki = [new (keyChar, 0, false, false, false)];
+        var expectedCki = new ConsoleKeyInfo (keyChar, consoleKey, shift, alt, control);
+
+        EscSeqUtils.DecodeEscSeq (
+                                  _escSeqReqProc,
+                                  ref _newConsoleKeyInfo,
+                                  ref _key,
+                                  _cki,
+                                  ref _mod,
+                                  out _c1Control,
+                                  out _code,
+                                  out _values,
+                                  out _terminating,
+                                  out _isKeyMouse,
+                                  out _mouseFlags,
+                                  out _pos,
+                                  out _seqReqStatus,
+                                  ProcessContinuousButtonPressed
+                                 );
+        Assert.Null (_escSeqReqProc);
+        Assert.Equal (expectedCki, _newConsoleKeyInfo);
+        Assert.Equal (consoleKey, _key);
+
+        ConsoleModifiers mods = new ();
+
+        if (shift)
+        {
+            mods = ConsoleModifiers.Shift;
+        }
+
+        if (alt)
+        {
+            mods |= ConsoleModifiers.Alt;
+        }
+
+        if (control)
+        {
+            mods |= ConsoleModifiers.Control;
+        }
+
+        Assert.Equal (mods, _mod);
+
+        if (keyChar == '\u001B')
+        {
+            Assert.Equal ("ESC", _c1Control);
+        }
+        else
+        {
+            Assert.Null (_c1Control);
+        }
+
+        Assert.Null (_code);
+        Assert.Null (_values);
+        Assert.Null (_terminating);
+        Assert.False (_isKeyMouse);
+        Assert.Equal (new () { 0 }, _mouseFlags);
+        Assert.Equal (Point.Empty, _pos);
+        Assert.Null (_seqReqStatus);
+        Assert.Equal (0, (int)_arg1);
+        Assert.Equal (Point.Empty, _arg2);
+
+        ClearAll ();
     }
 
     [Fact]
@@ -920,39 +1174,39 @@ public class EscSeqUtilsTests
     public void GetConsoleInputKey_ConsoleKeyInfo ()
     {
         var cki = new ConsoleKeyInfo ('r', 0, false, false, false);
-        var expectedCki = new ConsoleKeyInfo ('r', 0, false, false, false);
+        var expectedCki = new ConsoleKeyInfo ('r', ConsoleKey.R, false, false, false);
         Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki));
 
         cki = new ('r', 0, true, false, false);
-        expectedCki = new ('r', 0, true, false, false);
+        expectedCki = new ('r', ConsoleKey.R, true, false, false);
         Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki));
 
         cki = new ('r', 0, false, true, false);
-        expectedCki = new ('r', 0, false, true, false);
+        expectedCki = new ('r', ConsoleKey.R, false, true, false);
         Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki));
 
         cki = new ('r', 0, false, false, true);
-        expectedCki = new ('r', 0, false, false, true);
+        expectedCki = new ('r', ConsoleKey.R, false, false, true);
         Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki));
 
         cki = new ('r', 0, true, true, false);
-        expectedCki = new ('r', 0, true, true, false);
+        expectedCki = new ('r', ConsoleKey.R, true, true, false);
         Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki));
 
         cki = new ('r', 0, false, true, true);
-        expectedCki = new ('r', 0, false, true, true);
+        expectedCki = new ('r', ConsoleKey.R, false, true, true);
         Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki));
 
         cki = new ('r', 0, true, true, true);
-        expectedCki = new ('r', 0, true, true, true);
+        expectedCki = new ('r', ConsoleKey.R, true, true, true);
         Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki));
 
         cki = new ('\u0012', 0, false, false, false);
-        expectedCki = new ('R', ConsoleKey.R, false, false, true);
+        expectedCki = new ('\u0012', ConsoleKey.R, false, false, true);
         Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki));
 
         cki = new ('\0', (ConsoleKey)64, false, false, true);
-        expectedCki = new (' ', ConsoleKey.Spacebar, false, false, true);
+        expectedCki = new ('\0', ConsoleKey.Spacebar, false, false, true);
         Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki));
 
         cki = new ('\r', 0, false, false, false);
@@ -964,7 +1218,7 @@ public class EscSeqUtilsTests
         Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki));
 
         cki = new ('R', 0, false, false, false);
-        expectedCki = new ('R', 0, false, false, false);
+        expectedCki = new ('R', ConsoleKey.R, true, false, false);
         Assert.Equal (expectedCki, EscSeqUtils.MapConsoleKeyInfo (cki));
     }
 
@@ -999,18 +1253,19 @@ public class EscSeqUtilsTests
         Assert.Equal (ConsoleKey.F11, EscSeqUtils.GetConsoleKey ('~', "23", ref mod, ref keyChar));
         Assert.Equal (ConsoleKey.F12, EscSeqUtils.GetConsoleKey ('~', "24", ref mod, ref keyChar));
         Assert.Equal (0, (int)EscSeqUtils.GetConsoleKey ('~', "", ref mod, ref keyChar));
-        Assert.Equal (ConsoleKey.Add, EscSeqUtils.GetConsoleKey ('l', "", ref mod, ref keyChar));
-        Assert.Equal (ConsoleKey.Subtract, EscSeqUtils.GetConsoleKey ('m', "", ref mod, ref keyChar));
-        Assert.Equal (ConsoleKey.Insert, EscSeqUtils.GetConsoleKey ('p', "", ref mod, ref keyChar));
-        Assert.Equal (ConsoleKey.End, EscSeqUtils.GetConsoleKey ('q', "", ref mod, ref keyChar));
-        Assert.Equal (ConsoleKey.DownArrow, EscSeqUtils.GetConsoleKey ('r', "", ref mod, ref keyChar));
-        Assert.Equal (ConsoleKey.PageDown, EscSeqUtils.GetConsoleKey ('s', "", ref mod, ref keyChar));
-        Assert.Equal (ConsoleKey.LeftArrow, EscSeqUtils.GetConsoleKey ('t', "", ref mod, ref keyChar));
-        Assert.Equal (ConsoleKey.Clear, EscSeqUtils.GetConsoleKey ('u', "", ref mod, ref keyChar));
-        Assert.Equal (ConsoleKey.RightArrow, EscSeqUtils.GetConsoleKey ('v', "", ref mod, ref keyChar));
-        Assert.Equal (ConsoleKey.Home, EscSeqUtils.GetConsoleKey ('w', "", ref mod, ref keyChar));
-        Assert.Equal (ConsoleKey.UpArrow, EscSeqUtils.GetConsoleKey ('x', "", ref mod, ref keyChar));
-        Assert.Equal (ConsoleKey.PageUp, EscSeqUtils.GetConsoleKey ('y', "", ref mod, ref keyChar));
+        // These terminators are used by macOS on a numeric keypad without keys modifiers
+        Assert.Equal (ConsoleKey.Add, EscSeqUtils.GetConsoleKey ('l', null, ref mod, ref keyChar));
+        Assert.Equal (ConsoleKey.Subtract, EscSeqUtils.GetConsoleKey ('m', null, ref mod, ref keyChar));
+        Assert.Equal (ConsoleKey.Insert, EscSeqUtils.GetConsoleKey ('p', null, ref mod, ref keyChar));
+        Assert.Equal (ConsoleKey.End, EscSeqUtils.GetConsoleKey ('q', null, ref mod, ref keyChar));
+        Assert.Equal (ConsoleKey.DownArrow, EscSeqUtils.GetConsoleKey ('r', null, ref mod, ref keyChar));
+        Assert.Equal (ConsoleKey.PageDown, EscSeqUtils.GetConsoleKey ('s', null, ref mod, ref keyChar));
+        Assert.Equal (ConsoleKey.LeftArrow, EscSeqUtils.GetConsoleKey ('t', null, ref mod, ref keyChar));
+        Assert.Equal (ConsoleKey.Clear, EscSeqUtils.GetConsoleKey ('u', null, ref mod, ref keyChar));
+        Assert.Equal (ConsoleKey.RightArrow, EscSeqUtils.GetConsoleKey ('v', null, ref mod, ref keyChar));
+        Assert.Equal (ConsoleKey.Home, EscSeqUtils.GetConsoleKey ('w', null, ref mod, ref keyChar));
+        Assert.Equal (ConsoleKey.UpArrow, EscSeqUtils.GetConsoleKey ('x', null, ref mod, ref keyChar));
+        Assert.Equal (ConsoleKey.PageUp, EscSeqUtils.GetConsoleKey ('y', null, ref mod, ref keyChar));
     }
 
     [Fact]
@@ -1031,9 +1286,9 @@ public class EscSeqUtilsTests
     }
 
     [Fact]
-    public void GetEscapeResult_Tests ()
+    public void GetEscapeResult_Multiple_Tests ()
     {
-        char [] kChars = { '\u001b', '[', '5', ';', '1', '0', 'r' };
+        char [] kChars = ['\u001b', '[', '5', ';', '1', '0', 'r'];
         (_c1Control, _code, _values, _terminating) = EscSeqUtils.GetEscapeResult (kChars);
         Assert.Equal ("CSI", _c1Control);
         Assert.Null (_code);
@@ -1043,6 +1298,31 @@ public class EscSeqUtilsTests
         Assert.Equal ("r", _terminating);
     }
 
+    [Theory]
+    [InlineData ('\u001B')]
+    [InlineData (['\r'])]
+    [InlineData (['1'])]
+    [InlineData (['!'])]
+    [InlineData (['a'])]
+    [InlineData (['A'])]
+    public void GetEscapeResult_Single_Tests (params char [] kChars)
+    {
+        (_c1Control, _code, _values, _terminating) = EscSeqUtils.GetEscapeResult (kChars);
+
+        if (kChars [0] == '\u001B')
+        {
+            Assert.Equal ("ESC", _c1Control);
+        }
+        else
+        {
+            Assert.Null (_c1Control);
+        }
+
+        Assert.Null (_code);
+        Assert.Null (_values);
+        Assert.Null (_terminating);
+    }
+
     [Fact]
     public void GetKeyCharArray_Tests ()
     {
@@ -1160,6 +1440,73 @@ public class EscSeqUtilsTests
         Assert.Equal (cki, expectedCkInfos [0]);
     }
 
+    [Fact]
+    public void SplitEscapeRawString_Multiple_Tests ()
+    {
+        string rawData =
+            "\r[<35;50;1m[<35;49;1m[<35;47;1m[<35;46;1m[<35;45;2m[<35;44;2m[<35;43;3m[<35;42;3m[<35;41;4m[<35;40;5m[<35;39;6m[<35;49;1m[<35;48;2m[<0;33;6M[<0;33;6mOC\r";
+
+        List<string> splitList = EscSeqUtils.SplitEscapeRawString (rawData);
+        Assert.Equal (18, splitList.Count);
+        Assert.Equal ("\r", splitList [0]);
+        Assert.Equal ("\u001b[<35;50;1m", splitList [1]);
+        Assert.Equal ("\u001b[<35;49;1m", splitList [2]);
+        Assert.Equal ("\u001b[<35;47;1m", splitList [3]);
+        Assert.Equal ("\u001b[<35;46;1m", splitList [4]);
+        Assert.Equal ("\u001b[<35;45;2m", splitList [5]);
+        Assert.Equal ("\u001b[<35;44;2m", splitList [6]);
+        Assert.Equal ("\u001b[<35;43;3m", splitList [7]);
+        Assert.Equal ("\u001b[<35;42;3m", splitList [8]);
+        Assert.Equal ("\u001b[<35;41;4m", splitList [9]);
+        Assert.Equal ("\u001b[<35;40;5m", splitList [10]);
+        Assert.Equal ("\u001b[<35;39;6m", splitList [11]);
+        Assert.Equal ("\u001b[<35;49;1m", splitList [12]);
+        Assert.Equal ("\u001b[<35;48;2m", splitList [13]);
+        Assert.Equal ("\u001b[<0;33;6M", splitList [14]);
+        Assert.Equal ("\u001b[<0;33;6m", splitList [15]);
+        Assert.Equal ("\u001bOC", splitList [16]);
+        Assert.Equal ("\r", splitList [^1]);
+    }
+
+    [Theory]
+    [InlineData ("[<35;50;1m")]
+    [InlineData ("\r")]
+    [InlineData ("1")]
+    [InlineData ("!")]
+    [InlineData ("a")]
+    [InlineData ("A")]
+    public void SplitEscapeRawString_Single_Tests (string rawData)
+    {
+        List<string> splitList = EscSeqUtils.SplitEscapeRawString (rawData);
+        Assert.Single (splitList);
+        Assert.Equal (rawData, splitList [0]);
+    }
+
+    [Theory]
+    [InlineData (null, null, null, null)]
+    [InlineData ("\u001b[8;1", null, null, "\u001b[8;1")]
+    [InlineData (null, "\u001b[8;1", 5, "\u001b[8;1")]
+    [InlineData ("\u001b[8;1", null, 5, "\u001b[8;1")]
+    [InlineData ("\u001b[8;1", "0;20t", -1, "\u001b[8;10;20t")]
+    [InlineData ("\u001b[8;1", "0;20t", 0, "\u001b[8;10;20t")]
+    [InlineData ("0;20t", "\u001b[8;1", 5, "\u001b[8;10;20t")]
+    [InlineData ("0;20t", "\u001b[8;1", 3, "\u001b[80;20t;1")]
+    public void InsertArray_Tests (string toInsert, string current, int? index, string expected)
+    {
+        ConsoleKeyInfo [] toIns = EscSeqUtils.ToConsoleKeyInfoArray (toInsert);
+        ConsoleKeyInfo [] cki = EscSeqUtils.ToConsoleKeyInfoArray (current);
+        ConsoleKeyInfo [] result = EscSeqUtils.ToConsoleKeyInfoArray (expected);
+
+        if (index is null)
+        {
+            Assert.Equal (result, EscSeqUtils.InsertArray (toIns, cki));
+        }
+        else
+        {
+            Assert.Equal (result, EscSeqUtils.InsertArray (toIns, cki, (int)index));
+        }
+    }
+
     private void ClearAll ()
     {
         _escSeqReqProc = default (EscSeqRequests);