Skip to content

Commit

Permalink
Merge pull request #3542 from BDisp/v2_3540_unix_keyboard-fix
Browse files Browse the repository at this point in the history
Fixes #3540. V2: Keyboard input not working on Unix platforms
  • Loading branch information
tig committed Jun 17, 2024
2 parents bd00453 + 44ae053 commit 9e4169a
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 71 deletions.
14 changes: 7 additions & 7 deletions Terminal.Gui/Application/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ internal static void ResetState ()
/// </para>
/// <para>
/// The <see cref="Run{T}(Func{Exception, bool}, ConsoleDriver)"/> function combines
/// <see cref="Init(ConsoleDriver, string)"/> and <see cref="Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/>
/// <see cref="Init(ConsoleDriver, string)"/> and <see cref="Run(Toplevel, Func{Exception, bool})"/>
/// into a single
/// call. An application cam use <see cref="Run{T}(Func{Exception, bool}, ConsoleDriver)"/> without explicitly calling
/// <see cref="Init(ConsoleDriver, string)"/>.
Expand Down Expand Up @@ -344,7 +344,7 @@ public static List<Type> GetDriverTypes ()
/// <summary>Shutdown an application initialized with <see cref="Init"/>.</summary>
/// <remarks>
/// Shutdown must be called for every call to <see cref="Init"/> or
/// <see cref="Application.Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/> to ensure all resources are cleaned
/// <see cref="Application.Run(Toplevel, Func{Exception, bool})"/> to ensure all resources are cleaned
/// up (Disposed)
/// and terminal settings are restored.
/// </remarks>
Expand Down Expand Up @@ -639,7 +639,7 @@ internal static bool PositionCursor (View view)

/// <summary>
/// Runs the application by creating a <see cref="Toplevel"/> object and calling
/// <see cref="Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/>.
/// <see cref="Run(Toplevel, Func{Exception, bool})"/>.
/// </summary>
/// <remarks>
/// <para>Calling <see cref="Init"/> first is not needed as this function will initialize the application.</para>
Expand All @@ -656,7 +656,7 @@ internal static bool PositionCursor (View view)

/// <summary>
/// Runs the application by creating a <see cref="Toplevel"/>-derived object of type <c>T</c> and calling
/// <see cref="Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/>.
/// <see cref="Run(Toplevel, Func{Exception, bool})"/>.
/// </summary>
/// <remarks>
/// <para>Calling <see cref="Init"/> first is not needed as this function will initialize the application.</para>
Expand Down Expand Up @@ -698,11 +698,11 @@ public static T Run<T> (Func<Exception, bool> errorHandler = null, ConsoleDriver
/// modal <see cref="View"/>s such as <see cref="Dialog"/> boxes.
/// </para>
/// <para>
/// To make a <see cref="Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/> stop execution, call
/// To make a <see cref="Run(Toplevel, Func{Exception, bool})"/> stop execution, call
/// <see cref="Application.RequestStop"/>.
/// </para>
/// <para>
/// Calling <see cref="Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/> is equivalent to calling
/// Calling <see cref="Run(Toplevel, Func{Exception, bool})"/> is equivalent to calling
/// <see cref="Begin(Toplevel)"/>, followed by <see cref="RunLoop(RunState)"/>, and then calling
/// <see cref="End(RunState)"/>.
/// </para>
Expand Down Expand Up @@ -996,7 +996,7 @@ public static void RunIteration (ref RunState state, ref bool firstIteration)
/// <summary>Stops the provided <see cref="Toplevel"/>, causing or the <paramref name="top"/> if provided.</summary>
/// <param name="top">The <see cref="Toplevel"/> to stop.</param>
/// <remarks>
/// <para>This will cause <see cref="Application.Run(Toplevel, Func{Exception, bool}, ConsoleDriver)"/> to return.</para>
/// <para>This will cause <see cref="Application.Run(Toplevel, Func{Exception, bool})"/> to return.</para>
/// <para>
/// Calling <see cref="Application.RequestStop"/> is equivalent to setting the <see cref="Toplevel.Running"/>
/// property on the currently running <see cref="Toplevel"/> to false.
Expand Down
13 changes: 12 additions & 1 deletion Terminal.Gui/ConsoleDrivers/ConsoleKeyMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -859,13 +859,24 @@ public static KeyCode MapConsoleKeyInfoToKeyCode (ConsoleKeyInfo consoleKeyInfo)
case ConsoleKey.F24:
keyCode = KeyCode.F24;

break;
case ConsoleKey.Clear:
keyCode = KeyCode.Clear;

break;
case ConsoleKey.Tab:
keyCode = KeyCode.Tab;

break;
default:
keyCode = (KeyCode)consoleKeyInfo.KeyChar;
if ((int)consoleKeyInfo.KeyChar is >= 1 and <= 26)
{
keyCode = (KeyCode)(consoleKeyInfo.KeyChar + 64);
}
else
{
keyCode = (KeyCode)consoleKeyInfo.KeyChar;
}

break;
}
Expand Down
81 changes: 50 additions & 31 deletions Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -515,9 +515,10 @@ internal void ProcessInput ()
{
// The ESC-number handling, debatable.
// Simulates the AltMask itself by pressing Alt + Space.
// Needed for macOS
if (wch2 == (int)KeyCode.Space)
{
k = KeyCode.AltMask;
k = KeyCode.AltMask | KeyCode.Space;
}
else if (wch2 - (int)KeyCode.Space >= (uint)KeyCode.A
&& wch2 - (int)KeyCode.Space <= (uint)KeyCode.Z)
Expand All @@ -532,41 +533,51 @@ internal void ProcessInput ()
{
k = (KeyCode)((uint)KeyCode.AltMask + (uint)KeyCode.D0 + (wch2 - (uint)KeyCode.D0));
}
else if (wch2 == Curses.KeyCSI)
else
{
ConsoleKeyInfo [] cki =
{
new ((char)KeyCode.Esc, 0, false, false, false), new ('[', 0, false, false, false)
};
[
new ((char)KeyCode.Esc, 0, false, false, false), new ((char)wch2, 0, false, false, false)
];
HandleEscSeqResponse (ref code, ref k, ref wch2, ref key, ref cki);

return;
}
else
{
// Unfortunately there are no way to differentiate Ctrl+Alt+alfa and Ctrl+Shift+Alt+alfa.
if (((KeyCode)wch2 & KeyCode.CtrlMask) != 0)
{
k = (KeyCode)((uint)KeyCode.CtrlMask + (wch2 & ~(int)KeyCode.CtrlMask));
}

if (wch2 == 0)
{
k = KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.Space;
}
else if (wch >= (uint)KeyCode.A && wch <= (uint)KeyCode.Z)
{
k = KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.Space;
}
else if (wch2 < 256)
{
k = (KeyCode)wch2; // | KeyCode.AltMask;
}
else
{
k = (KeyCode)((uint)(KeyCode.AltMask | KeyCode.CtrlMask) + wch2);
}
}
//else if (wch2 == Curses.KeyCSI)
//{
// ConsoleKeyInfo [] cki =
// {
// new ((char)KeyCode.Esc, 0, false, false, false), new ('[', 0, false, false, false)
// };
// HandleEscSeqResponse (ref code, ref k, ref wch2, ref key, ref cki);

// return;
//}
//else
//{
// // Unfortunately there are no way to differentiate Ctrl+Alt+alfa and Ctrl+Shift+Alt+alfa.
// if (((KeyCode)wch2 & KeyCode.CtrlMask) != 0)
// {
// k = (KeyCode)((uint)KeyCode.CtrlMask + (wch2 & ~(int)KeyCode.CtrlMask));
// }

// if (wch2 == 0)
// {
// k = KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.Space;
// }
// //else if (wch >= (uint)KeyCode.A && wch <= (uint)KeyCode.Z)
// //{
// // k = KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.Space;
// //}
// else if (wch2 < 256)
// {
// k = (KeyCode)wch2; // | KeyCode.AltMask;
// }
// else
// {
// k = (KeyCode)((uint)(KeyCode.AltMask | KeyCode.CtrlMask) + wch2);
// }
//}

key = new Key (k);
}
Expand All @@ -584,6 +595,13 @@ internal void ProcessInput ()
OnKeyDown (new Key (k));
OnKeyUp (new Key (k));
}
else if (wch == 127)
{
// Backspace needed for macOS
k = KeyCode.Backspace;
OnKeyDown (new Key (k));
OnKeyUp (new Key (k));
}
else
{
// Unfortunately there are no way to differentiate Ctrl+alfa and Ctrl+Shift+alfa.
Expand Down Expand Up @@ -611,7 +629,8 @@ internal void ProcessInput ()
}

// Strip the KeyCode.Space flag off if it's set
if (k != KeyCode.Space && k.HasFlag (KeyCode.Space))
//if (k != KeyCode.Space && k.HasFlag (KeyCode.Space))
if (Key.GetIsKeyCodeAtoZ (k) && (k & KeyCode.Space) != 0)
{
k &= ~KeyCode.Space;
}
Expand Down
1 change: 0 additions & 1 deletion Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ public enum Event : long
public const int KeyPPage = 0x153;
public const int KeyHome = 0x106;
public const int KeyMouse = 0x199;
public const int KeyCSI = 0x5b;
public const int KeyEnd = 0x168;
public const int KeyDeleteChar = 0x14a;
public const int KeyInsertChar = 0x14b;
Expand Down
36 changes: 30 additions & 6 deletions Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ Action<MouseFlags, Point> continuousButtonPressedHandler
buttonState = new List<MouseFlags> { 0 };
pos = default (Point);
isResponse = false;
char keyChar = '\0';

switch (c1Control)
{
Expand Down Expand Up @@ -242,10 +243,10 @@ Action<MouseFlags, Point> continuousButtonPressedHandler

break;
case "SS3":
key = GetConsoleKey (terminator [0], values [0], ref mod);
key = GetConsoleKey (terminator [0], values [0], ref mod, ref keyChar);

newConsoleKeyInfo = new ConsoleKeyInfo (
'\0',
keyChar,
key,
(mod & ConsoleModifiers.Shift) != 0,
(mod & ConsoleModifiers.Alt) != 0,
Expand All @@ -271,15 +272,15 @@ Action<MouseFlags, Point> continuousButtonPressedHandler

if (!string.IsNullOrEmpty (terminator))
{
key = GetConsoleKey (terminator [0], values [0], ref mod);
key = GetConsoleKey (terminator [0], values [0], ref mod, ref keyChar);

if (key != 0 && values.Length > 1)
{
mod |= GetConsoleModifiers (values [1]);
}

newConsoleKeyInfo = new ConsoleKeyInfo (
'\0',
keyChar,
key,
(mod & ConsoleModifiers.Shift) != 0,
(mod & ConsoleModifiers.Alt) != 0,
Expand Down Expand Up @@ -342,15 +343,26 @@ public static string GetC1ControlChar (in char c)
/// <see cref="CSI_SendDeviceAttributes2"/>.
/// </param>
/// <param name="value">The value.</param>
/// <param name="mod">The <see cref="ConsoleModifiers"/> which may changes.</param>
/// <param name="mod">The <see cref="ConsoleModifiers"/> which may change.</param>
/// <param name="keyChar">Normally is '\0' but on some cases may need other value.</param>
/// <returns>The <see cref="ConsoleKey"/> and probably the <see cref="ConsoleModifiers"/>.</returns>
public static ConsoleKey GetConsoleKey (char terminator, string? value, ref ConsoleModifiers mod)
public static ConsoleKey GetConsoleKey (char terminator, string? value, ref ConsoleModifiers mod, ref char keyChar)
{
if (terminator == 'Z')
{
mod |= ConsoleModifiers.Shift;
}

if (terminator == 'l')
{
keyChar = '+';
}

if (terminator == 'm')
{
keyChar = '-';
}

return (terminator, value) switch
{
('A', _) => ConsoleKey.UpArrow,
Expand All @@ -376,6 +388,18 @@ public static ConsoleKey GetConsoleKey (char terminator, string? value, ref Cons
('~', "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,
(_, _) => 0
};
}
Expand Down
2 changes: 2 additions & 0 deletions Terminal.Gui/Views/Menu/MenuBar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ public MenuBar ()

// TODO: Why do we have two keybindings for opening the menu? Ctrl-Space and Key?
KeyBindings.Add (Key.Space.WithCtrl, keyBinding);
// This is needed for macOS because Key.Space.WithCtrl doesn't work
KeyBindings.Add (Key.Space.WithAlt, keyBinding);

// TODO: Figure out how to make Alt work (on Windows)
//KeyBindings.Add (Key.WithAlt, keyBinding);
Expand Down
2 changes: 2 additions & 0 deletions UICatalog/Scenarios/Buttons.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public class Buttons : Scenario
{
public override void Main ()
{
Application.Init ();

Window main = new ()
{
Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"
Expand Down
Loading

0 comments on commit 9e4169a

Please sign in to comment.