Kaynağa Gözat

Cleans up key handling in drivers (was "fixes VkeyPacketSimulator is broken") (#3078)

* Fixes #3054. VkeyPacketSimulator scenario is broken.

* Fix some key handle and unit tests.

* Remove unnecessary conditional.

* Improves key handling.

* Also allow map capslock to shift with accented characters.

* Change to MemberData.

* Remove unnecessary using.

* Fix merge errors.

* Fixes #3095. WindowsDriver should return the mask keys to IsShift, IsAlt and IsCtrl return the right value.

* Modifiers keys are valid to be handled on key down and key up.

* Map KeyCode.Enter to ConsoleKey.Enter and vice versa.

* Updated ScanCodeMapping table with readable constants

* Documented bugs

* Implemented mapping using MapVirtualKeyEx

* Implemented mapping using MapVirtualKeyEx

* Changed KeyCode special keys to match ConsoleKey values + max unicode codepoint

* Fixed bogus CollectionNavigator impl and tests

* Nuked DeleteChar. renamed InsertChar to Insert

* KeyCode.Enter = ConsoleKey.Enter, not \n

* Code cleanup

* Added diag for keyboard layout name

* Fixed AltGr support (hopefully)

* Simplified code

* Simplified KeyCode by removing ShiftKeys

* Fixed TextView

* Code cleanup

* Fixes cursesdriver (somewhat)

* Code cleanup

* netdriver wip

* Fixed netdriver under WSL

* Turned off debug spew

* Removed old code

---------

Co-authored-by: Tig Kindel <[email protected]>
Co-authored-by: Tig Kindel <[email protected]>
BDisp 1 yıl önce
ebeveyn
işleme
0484fc8bf9
32 değiştirilmiş dosya ile 4563 ekleme ve 3150 silme
  1. 2 0
      Terminal.Gui/Configuration/KeyJsonConverter.cs
  2. 156 120
      Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs
  3. 1665 541
      Terminal.Gui/ConsoleDrivers/ConsoleKeyMapping.cs
  4. 71 87
      Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
  5. 1 1
      Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs
  6. 22 22
      Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
  7. 298 319
      Terminal.Gui/ConsoleDrivers/NetDriver.cs
  8. 157 167
      Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
  9. 64 60
      Terminal.Gui/Input/Key.cs
  10. 1 0
      Terminal.Gui/Terminal.Gui.csproj
  11. 179 182
      Terminal.Gui/Text/CollectionNavigatorBase.cs
  12. 9 3
      Terminal.Gui/View/ViewKeyboard.cs
  13. 1 1
      Terminal.Gui/Views/DateField.cs
  14. 1 1
      Terminal.Gui/Views/FileDialog.cs
  15. 3 4
      Terminal.Gui/Views/TextField.cs
  16. 1 1
      Terminal.Gui/Views/TextValidateField.cs
  17. 1307 1317
      Terminal.Gui/Views/TextView.cs
  18. 1 1
      Terminal.Gui/Views/TimeField.cs
  19. 3 3
      UICatalog/Scenarios/CsvEditor.cs
  20. 1 1
      UICatalog/Scenarios/InteractiveTree.cs
  21. 1 1
      UICatalog/Scenarios/ListColumns.cs
  22. 1 1
      UICatalog/Scenarios/TableEditor.cs
  23. 55 69
      UICatalog/Scenarios/VkeyPacketSimulator.cs
  24. 415 148
      UnitTests/ConsoleDrivers/ConsoleKeyMappingTests.cs
  25. 81 31
      UnitTests/Input/KeyTests.cs
  26. 17 18
      UnitTests/Text/CollectionNavigatorTests.cs
  27. 12 12
      UnitTests/Text/TextFormatterTests.cs
  28. 12 12
      UnitTests/View/HotKeyTests.cs
  29. 1 1
      UnitTests/Views/DateFieldTests.cs
  30. 5 5
      UnitTests/Views/TextFieldTests.cs
  31. 19 20
      UnitTests/Views/TextViewTests.cs
  32. 1 1
      UnitTests/Views/TimeFieldTests.cs

+ 2 - 0
Terminal.Gui/Configuration/KeyJsonConverter.cs

@@ -8,7 +8,9 @@ namespace Terminal.Gui;
 /// Support for <see cref="Key"/> in JSON in the form of "Ctrl-X" or "Alt-Shift-F1".
 /// Support for <see cref="Key"/> in JSON in the form of "Ctrl-X" or "Alt-Shift-F1".
 /// </summary>
 /// </summary>
 public class KeyJsonConverter : JsonConverter<Key> {
 public class KeyJsonConverter : JsonConverter<Key> {
+	/// <inheritdoc />
 	public override Key Read (ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => Key.TryParse (reader.GetString (), out var key) ? key : Key.Empty;
 	public override Key Read (ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => Key.TryParse (reader.GetString (), out var key) ? key : Key.Empty;
 
 
+	/// <inheritdoc />
 	public override void Write (Utf8JsonWriter writer, Key value, JsonSerializerOptions options) => writer.WriteStringValue (value.ToString ());
 	public override void Write (Utf8JsonWriter writer, Key value, JsonSerializerOptions options) => writer.WriteStringValue (value.ToString ());
 }
 }

+ 156 - 120
Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs

@@ -5,6 +5,7 @@ using System.Text;
 using System;
 using System;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Linq;
 using System.Linq;
+using Terminal.Gui.ConsoleDrivers;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
@@ -31,7 +32,6 @@ public abstract class ConsoleDriver {
 	internal static bool RunningUnitTests { get; set; }
 	internal static bool RunningUnitTests { get; set; }
 
 
 	#region Setup & Teardown
 	#region Setup & Teardown
-
 	/// <summary>
 	/// <summary>
 	/// Initializes the driver
 	/// Initializes the driver
 	/// </summary>
 	/// </summary>
@@ -42,7 +42,6 @@ public abstract class ConsoleDriver {
 	/// Ends the execution of the console driver.
 	/// Ends the execution of the console driver.
 	/// </summary>
 	/// </summary>
 	internal abstract void End ();
 	internal abstract void End ();
-
 	#endregion
 	#endregion
 
 
 	/// <summary>
 	/// <summary>
@@ -129,10 +128,7 @@ public abstract class ConsoleDriver {
 	/// <param name="rune"></param>
 	/// <param name="rune"></param>
 	/// <returns><see langword="true"/> if the rune can be properly presented; <see langword="false"/> if the driver
 	/// <returns><see langword="true"/> if the rune can be properly presented; <see langword="false"/> if the driver
 	/// does not support displaying this rune.</returns>
 	/// does not support displaying this rune.</returns>
-	public virtual bool IsRuneSupported (Rune rune)
-	{
-		return Rune.IsValid (rune.Value);
-	}
+	public virtual bool IsRuneSupported (Rune rune) => Rune.IsValid (rune.Value);
 
 
 	/// <summary>
 	/// <summary>
 	/// Adds the specified rune to the display at the current cursor position. 
 	/// Adds the specified rune to the display at the current cursor position. 
@@ -151,7 +147,7 @@ public abstract class ConsoleDriver {
 	public void AddRune (Rune rune)
 	public void AddRune (Rune rune)
 	{
 	{
 		int runeWidth = -1;
 		int runeWidth = -1;
-		var validLocation = IsValidLocation (Col, Row);
+		bool validLocation = IsValidLocation (Col, Row);
 		if (validLocation) {
 		if (validLocation) {
 			rune = rune.MakePrintable ();
 			rune = rune.MakePrintable ();
 			runeWidth = rune.GetColumns ();
 			runeWidth = rune.GetColumns ();
@@ -275,7 +271,7 @@ public abstract class ConsoleDriver {
 	public void AddStr (string str)
 	public void AddStr (string str)
 	{
 	{
 		var runes = str.EnumerateRunes ().ToList ();
 		var runes = str.EnumerateRunes ().ToList ();
-		for (var i = 0; i < runes.Count; i++) {
+		for (int i = 0; i < runes.Count; i++) {
 			//if (runes [i].IsCombiningMark()) {
 			//if (runes [i].IsCombiningMark()) {
 
 
 			//	// Attempt to normalize
 			//	// Attempt to normalize
@@ -361,8 +357,8 @@ public abstract class ConsoleDriver {
 		lock (Contents) {
 		lock (Contents) {
 			// Can raise an exception while is still resizing.
 			// Can raise an exception while is still resizing.
 			try {
 			try {
-				for (var row = 0; row < Rows; row++) {
-					for (var c = 0; c < Cols; c++) {
+				for (int row = 0; row < Rows; row++) {
+					for (int c = 0; c < Cols; c++) {
 						Contents [row, c] = new Cell () {
 						Contents [row, c] = new Cell () {
 							Rune = (Rune)' ',
 							Rune = (Rune)' ',
 							Attribute = new Attribute (Color.White, Color.Black),
 							Attribute = new Attribute (Color.White, Color.Black),
@@ -381,11 +377,10 @@ public abstract class ConsoleDriver {
 	public abstract void UpdateScreen ();
 	public abstract void UpdateScreen ();
 
 
 	#region Color Handling
 	#region Color Handling
-
 	/// <summary>
 	/// <summary>
 	/// Gets whether the <see cref="ConsoleDriver"/> supports TrueColor output.
 	/// Gets whether the <see cref="ConsoleDriver"/> supports TrueColor output.
 	/// </summary>
 	/// </summary>
-	public virtual bool SupportsTrueColor { get => true; }
+	public virtual bool SupportsTrueColor => true;
 
 
 	/// <summary>
 	/// <summary>
 	/// Gets or sets whether the <see cref="ConsoleDriver"/> should use 16 colors instead of the default TrueColors. See <see cref="Application.Force16Colors"/>
 	/// Gets or sets whether the <see cref="ConsoleDriver"/> should use 16 colors instead of the default TrueColors. See <see cref="Application.Force16Colors"/>
@@ -399,7 +394,7 @@ public abstract class ConsoleDriver {
 	/// </remarks>
 	/// </remarks>
 	internal virtual bool Force16Colors {
 	internal virtual bool Force16Colors {
 		get => Application.Force16Colors || !SupportsTrueColor;
 		get => Application.Force16Colors || !SupportsTrueColor;
-		set => Application.Force16Colors = (value || !SupportsTrueColor);
+		set => Application.Force16Colors = value || !SupportsTrueColor;
 	}
 	}
 
 
 	Attribute _currentAttribute;
 	Attribute _currentAttribute;
@@ -447,17 +442,13 @@ public abstract class ConsoleDriver {
 	/// <param name="foreground">The foreground color.</param>
 	/// <param name="foreground">The foreground color.</param>
 	/// <param name="background">The background color.</param>
 	/// <param name="background">The background color.</param>
 	/// <returns>The attribute for the foreground and background colors.</returns>
 	/// <returns>The attribute for the foreground and background colors.</returns>
-	public virtual Attribute MakeColor (Color foreground, Color background)
-	{
+	public virtual Attribute MakeColor (Color foreground, Color background) =>
 		// Encode the colors into the int value.
 		// Encode the colors into the int value.
-		return new Attribute (
-			platformColor: 0, // only used by cursesdriver!
-			foreground: foreground,
-			background: background
+		new (
+			0, // only used by cursesdriver!
+			foreground,
+			background
 		);
 		);
-	}
-
-
 	#endregion
 	#endregion
 
 
 	#region Mouse and Keyboard
 	#region Mouse and Keyboard
@@ -509,7 +500,6 @@ public abstract class ConsoleDriver {
 	/// <param name="alt">If <see langword="true"/> simulates the Alt key being pressed.</param>
 	/// <param name="alt">If <see langword="true"/> simulates the Alt key being pressed.</param>
 	/// <param name="ctrl">If <see langword="true"/> simulates the Ctrl key being pressed.</param>
 	/// <param name="ctrl">If <see langword="true"/> simulates the Ctrl key being pressed.</param>
 	public abstract void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool ctrl);
 	public abstract void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool ctrl);
-
 	#endregion
 	#endregion
 
 
 	/// <summary>
 	/// <summary>
@@ -521,16 +511,18 @@ public abstract class ConsoleDriver {
 		/// All diagnostics off
 		/// All diagnostics off
 		/// </summary>
 		/// </summary>
 		Off = 0b_0000_0000,
 		Off = 0b_0000_0000,
+
 		/// <summary>
 		/// <summary>
 		/// When enabled, <see cref="Frame.OnDrawFrames"/> will draw a 
 		/// When enabled, <see cref="Frame.OnDrawFrames"/> will draw a 
 		/// ruler in the frame for any side with a padding value greater than 0.
 		/// ruler in the frame for any side with a padding value greater than 0.
 		/// </summary>
 		/// </summary>
 		FrameRuler = 0b_0000_0001,
 		FrameRuler = 0b_0000_0001,
+
 		/// <summary>
 		/// <summary>
 		/// When enabled, <see cref="Frame.OnDrawFrames"/> will draw a 
 		/// When enabled, <see cref="Frame.OnDrawFrames"/> will draw a 
 		/// 'L', 'R', 'T', and 'B' when clearing <see cref="Thickness"/>'s instead of ' '.
 		/// 'L', 'R', 'T', and 'B' when clearing <see cref="Thickness"/>'s instead of ' '.
 		/// </summary>
 		/// </summary>
-		FramePadding = 0b_0000_0010,
+		FramePadding = 0b_0000_0010
 	}
 	}
 
 
 	/// <summary>
 	/// <summary>
@@ -552,8 +544,8 @@ public abstract class ConsoleDriver {
 	/// <param name="rune"></param>
 	/// <param name="rune"></param>
 	public void FillRect (Rect rect, Rune rune = default)
 	public void FillRect (Rect rect, Rune rune = default)
 	{
 	{
-		for (var r = rect.Y; r < rect.Y + rect.Height; r++) {
-			for (var c = rect.X; c < rect.X + rect.Width; c++) {
+		for (int r = rect.Y; r < rect.Y + rect.Height; r++) {
+			for (int c = rect.X; c < rect.X + rect.Width; c++) {
 				Application.Driver.Move (c, r);
 				Application.Driver.Move (c, r);
 				Application.Driver.AddRune (rune == default ? new Rune (' ') : rune);
 				Application.Driver.AddRune (rune == default ? new Rune (' ') : rune);
 			}
 			}
@@ -575,7 +567,6 @@ public abstract class ConsoleDriver {
 	public virtual string GetVersionInfo () => GetType ().Name;
 	public virtual string GetVersionInfo () => GetType ().Name;
 }
 }
 
 
-
 /// <summary>
 /// <summary>
 /// Terminal Cursor Visibility settings.
 /// Terminal Cursor Visibility settings.
 /// </summary>
 /// </summary>
@@ -631,10 +622,9 @@ public enum CursorVisibility {
 	///	Cursor caret is displayed a block ▉
 	///	Cursor caret is displayed a block ▉
 	/// </summary>
 	/// </summary>
 	/// <remarks>Works under Xterm-like terminal otherwise this is equivalent to <see ref="Block"/></remarks>
 	/// <remarks>Works under Xterm-like terminal otherwise this is equivalent to <see ref="Block"/></remarks>
-	BoxFix = 0x02020164,
+	BoxFix = 0x02020164
 }
 }
 
 
-
 /// <summary>
 /// <summary>
 /// The <see cref="KeyCode"/> enumeration encodes key information from <see cref="ConsoleDriver"/>s and provides a consistent
 /// The <see cref="KeyCode"/> enumeration encodes key information from <see cref="ConsoleDriver"/>s and provides a consistent
 /// way for application code to specify keys and receive key events. 
 /// way for application code to specify keys and receive key events. 
@@ -671,72 +661,59 @@ public enum CursorVisibility {
 [Flags]
 [Flags]
 public enum KeyCode : uint {
 public enum KeyCode : uint {
 	/// <summary>
 	/// <summary>
-	/// Mask that indicates that this is a character value, values outside this range
-	/// indicate special characters like Alt-key combinations or special keys on the
-	/// keyboard like function keys, arrows keys and so on.
+	/// Mask that indicates that the key is a unicode codepoint. Values outside this range
+	/// indicate the key has shift modifiers or is a special key like function keys, arrows keys and so on.
 	/// </summary>
 	/// </summary>
-	CharMask = 0xfffff,
+	CharMask = 0x_f_ffff,
 
 
 	/// <summary>
 	/// <summary>
 	/// If the <see cref="SpecialMask"/> is set, then the value is that of the special mask,
 	/// If the <see cref="SpecialMask"/> is set, then the value is that of the special mask,
 	/// otherwise, the value is in the the lower bits (as extracted by <see cref="CharMask"/>).
 	/// otherwise, the value is in the the lower bits (as extracted by <see cref="CharMask"/>).
 	/// </summary>
 	/// </summary>
-	SpecialMask = 0xfff00000,
+	SpecialMask = 0x_fff0_0000,
 
 
 	/// <summary>
 	/// <summary>
-	/// The key code representing null or empty
-	/// </summary>
-	Null = '\0',
-
-	/// <summary>
-	/// Backspace key.
+	/// When this value is set, the Key encodes the sequence Shift-KeyValue.
+	/// The actual value must be extracted by removing the ShiftMask.
 	/// </summary>
 	/// </summary>
-	Backspace = 8,
+	ShiftMask = 0x_1000_0000,
 
 
 	/// <summary>
 	/// <summary>
-	/// The key code for the tab key (forwards tab key).
+	/// When this value is set, the Key encodes the sequence Alt-KeyValue.
+	/// The actual value must be extracted by removing the AltMask.
 	/// </summary>
 	/// </summary>
-	Tab = 9,
+	AltMask = 0x_8000_0000,
 
 
 	/// <summary>
 	/// <summary>
-	/// The key code for the return key.
+	/// When this value is set, the Key encodes the sequence Ctrl-KeyValue.
+	/// The actual value must be extracted by removing the CtrlMask.
 	/// </summary>
 	/// </summary>
-	Enter = '\n',
+	CtrlMask = 0x_4000_0000,
 
 
 	/// <summary>
 	/// <summary>
-	/// The key code for the clear key.
+	/// The key code representing an invalid or empty key.
 	/// </summary>
 	/// </summary>
-	Clear = 12,
+	Null = 0,
 
 
 	/// <summary>
 	/// <summary>
-	/// The key code for the Shift key.
+	/// Backspace key.
 	/// </summary>
 	/// </summary>
-	ShiftKey = 16,
+	Backspace = 8,
 
 
 	/// <summary>
 	/// <summary>
-	/// The key code for the Ctrl key.
+	/// The key code for the tab key (forwards tab key).
 	/// </summary>
 	/// </summary>
-	CtrlKey = 17,
+	Tab = 9,
 
 
 	/// <summary>
 	/// <summary>
-	/// The key code for the Alt key.
+	/// The key code for the return key.
 	/// </summary>
 	/// </summary>
-	AltKey = 18,
+	Enter = ConsoleKey.Enter,
 
 
 	/// <summary>
 	/// <summary>
-	/// The key code for the CapsLock key.
+	/// The key code for the clear key.
 	/// </summary>
 	/// </summary>
-	CapsLock = 20,
-
-	///// <summary>
-	///// The key code for the NumLock key.
-	///// </summary>
-	//NumLock = 144,
-
-	///// <summary>
-	///// The key code for the ScrollLock key.
-	///// </summary>
-	//ScrollLock = 145,
+	Clear = 12,
 
 
 	/// <summary>
 	/// <summary>
 	/// The key code for the escape key.
 	/// The key code for the escape key.
@@ -752,38 +729,47 @@ public enum KeyCode : uint {
 	/// Digit 0.
 	/// Digit 0.
 	/// </summary>
 	/// </summary>
 	D0 = 48,
 	D0 = 48,
+
 	/// <summary>
 	/// <summary>
 	/// Digit 1.
 	/// Digit 1.
 	/// </summary>
 	/// </summary>
 	D1,
 	D1,
+
 	/// <summary>
 	/// <summary>
 	/// Digit 2.
 	/// Digit 2.
 	/// </summary>
 	/// </summary>
 	D2,
 	D2,
+
 	/// <summary>
 	/// <summary>
 	/// Digit 3.
 	/// Digit 3.
 	/// </summary>
 	/// </summary>
 	D3,
 	D3,
+
 	/// <summary>
 	/// <summary>
 	/// Digit 4.
 	/// Digit 4.
 	/// </summary>
 	/// </summary>
 	D4,
 	D4,
+
 	/// <summary>
 	/// <summary>
 	/// Digit 5.
 	/// Digit 5.
 	/// </summary>
 	/// </summary>
 	D5,
 	D5,
+
 	/// <summary>
 	/// <summary>
 	/// Digit 6.
 	/// Digit 6.
 	/// </summary>
 	/// </summary>
 	D6,
 	D6,
+
 	/// <summary>
 	/// <summary>
 	/// Digit 7.
 	/// Digit 7.
 	/// </summary>
 	/// </summary>
 	D7,
 	D7,
+
 	/// <summary>
 	/// <summary>
 	/// Digit 8.
 	/// Digit 8.
 	/// </summary>
 	/// </summary>
 	D8,
 	D8,
+
 	/// <summary>
 	/// <summary>
 	/// Digit 9.
 	/// Digit 9.
 	/// </summary>
 	/// </summary>
@@ -793,271 +779,321 @@ public enum KeyCode : uint {
 	/// The key code for the A key
 	/// The key code for the A key
 	/// </summary>
 	/// </summary>
 	A = 65,
 	A = 65,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the B key
 	/// The key code for the B key
 	/// </summary>
 	/// </summary>
 	B,
 	B,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the C key
 	/// The key code for the C key
 	/// </summary>
 	/// </summary>
 	C,
 	C,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the D key
 	/// The key code for the D key
 	/// </summary>
 	/// </summary>
 	D,
 	D,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the E key
 	/// The key code for the E key
 	/// </summary>
 	/// </summary>
 	E,
 	E,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the F key
 	/// The key code for the F key
 	/// </summary>
 	/// </summary>
 	F,
 	F,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the G key
 	/// The key code for the G key
 	/// </summary>
 	/// </summary>
 	G,
 	G,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the H key
 	/// The key code for the H key
 	/// </summary>
 	/// </summary>
 	H,
 	H,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the I key
 	/// The key code for the I key
 	/// </summary>
 	/// </summary>
 	I,
 	I,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the J key
 	/// The key code for the J key
 	/// </summary>
 	/// </summary>
 	J,
 	J,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the K key
 	/// The key code for the K key
 	/// </summary>
 	/// </summary>
 	K,
 	K,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the L key
 	/// The key code for the L key
 	/// </summary>
 	/// </summary>
 	L,
 	L,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the M key
 	/// The key code for the M key
 	/// </summary>
 	/// </summary>
 	M,
 	M,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the N key
 	/// The key code for the N key
 	/// </summary>
 	/// </summary>
 	N,
 	N,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the O key
 	/// The key code for the O key
 	/// </summary>
 	/// </summary>
 	O,
 	O,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the P key
 	/// The key code for the P key
 	/// </summary>
 	/// </summary>
 	P,
 	P,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the Q key
 	/// The key code for the Q key
 	/// </summary>
 	/// </summary>
 	Q,
 	Q,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the R key
 	/// The key code for the R key
 	/// </summary>
 	/// </summary>
 	R,
 	R,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the S key
 	/// The key code for the S key
 	/// </summary>
 	/// </summary>
 	S,
 	S,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the T key
 	/// The key code for the T key
 	/// </summary>
 	/// </summary>
 	T,
 	T,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the U key
 	/// The key code for the U key
 	/// </summary>
 	/// </summary>
 	U,
 	U,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the V key
 	/// The key code for the V key
 	/// </summary>
 	/// </summary>
 	V,
 	V,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the W key
 	/// The key code for the W key
 	/// </summary>
 	/// </summary>
 	W,
 	W,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the X key
 	/// The key code for the X key
 	/// </summary>
 	/// </summary>
 	X,
 	X,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the Y key
 	/// The key code for the Y key
 	/// </summary>
 	/// </summary>
 	Y,
 	Y,
+
 	/// <summary>
 	/// <summary>
 	/// The key code for the Z key
 	/// The key code for the Z key
 	/// </summary>
 	/// </summary>
 	Z,
 	Z,
-	/// <summary>
-	/// The key code for the Delete key.
-	/// </summary>
-	Delete = 127,
 
 
-	/// <summary>
-	/// When this value is set, the Key encodes the sequence Shift-KeyValue.
-	/// </summary>
-	ShiftMask = 0x10000000,
+	///// <summary>
+	///// The key code for the Delete key.
+	///// </summary>
+	//Delete = 127,
 
 
-	/// <summary>
-	///   When this value is set, the Key encodes the sequence Alt-KeyValue.
-	///   And the actual value must be extracted by removing the AltMask.
-	/// </summary>
-	AltMask = 0x80000000,
+	// --- Special keys ---
+	// The values below are common non-alphanum keys. Their values are 
+	// based on the .NET ConsoleKey values, which, in-turn are based on the
+	// VK_ values from the Windows API. 
+	// We add MaxCodePoint to avoid conflicts with the Unicode values.
 
 
 	/// <summary>
 	/// <summary>
-	///   When this value is set, the Key encodes the sequence Ctrl-KeyValue.
-	///   And the actual value must be extracted by removing the CtrlMask.
+	/// The maximum Unicode codepoint value. Used to encode the non-alphanumeric control
+	/// keys. 
 	/// </summary>
 	/// </summary>
-	CtrlMask = 0x40000000,
+	MaxCodePoint = 0x10FFFF,
 
 
 	/// <summary>
 	/// <summary>
 	/// Cursor up key
 	/// Cursor up key
 	/// </summary>
 	/// </summary>
-	CursorUp = 0x100000,
+	CursorUp = MaxCodePoint + ConsoleKey.UpArrow,
+
 	/// <summary>
 	/// <summary>
 	/// Cursor down key.
 	/// Cursor down key.
 	/// </summary>
 	/// </summary>
-	CursorDown,
+	CursorDown = MaxCodePoint + ConsoleKey.DownArrow,
+
 	/// <summary>
 	/// <summary>
 	/// Cursor left key.
 	/// Cursor left key.
 	/// </summary>
 	/// </summary>
-	CursorLeft,
+	CursorLeft = MaxCodePoint + ConsoleKey.LeftArrow,
+
 	/// <summary>
 	/// <summary>
 	/// Cursor right key.
 	/// Cursor right key.
 	/// </summary>
 	/// </summary>
-	CursorRight,
+	CursorRight = MaxCodePoint + ConsoleKey.RightArrow,
+
 	/// <summary>
 	/// <summary>
 	/// Page Up key.
 	/// Page Up key.
 	/// </summary>
 	/// </summary>
-	PageUp,
+	PageUp = MaxCodePoint + ConsoleKey.PageUp,
+
 	/// <summary>
 	/// <summary>
 	/// Page Down key.
 	/// Page Down key.
 	/// </summary>
 	/// </summary>
-	PageDown,
+	PageDown = MaxCodePoint + ConsoleKey.PageDown,
+
 	/// <summary>
 	/// <summary>
 	/// Home key.
 	/// Home key.
 	/// </summary>
 	/// </summary>
-	Home,
+	Home = MaxCodePoint + ConsoleKey.Home,
+
 	/// <summary>
 	/// <summary>
 	/// End key.
 	/// End key.
 	/// </summary>
 	/// </summary>
-	End,
+	End = MaxCodePoint + ConsoleKey.End,
 
 
 	/// <summary>
 	/// <summary>
-	/// Insert character key.
+	/// Insert (INS) key.
 	/// </summary>
 	/// </summary>
-	InsertChar,
+	Insert = MaxCodePoint + ConsoleKey.Insert,
 
 
 	/// <summary>
 	/// <summary>
-	/// Delete character key.
+	/// Delete (DEL) key.
 	/// </summary>
 	/// </summary>
-	DeleteChar,
+	Delete = MaxCodePoint + ConsoleKey.Delete,
 
 
 	/// <summary>
 	/// <summary>
 	/// Print screen character key.
 	/// Print screen character key.
 	/// </summary>
 	/// </summary>
-	PrintScreen,
+	PrintScreen = MaxCodePoint + ConsoleKey.PrintScreen,
 
 
 	/// <summary>
 	/// <summary>
 	/// F1 key.
 	/// F1 key.
 	/// </summary>
 	/// </summary>
-	F1,
+	F1 = MaxCodePoint + ConsoleKey.F1,
+
 	/// <summary>
 	/// <summary>
 	/// F2 key.
 	/// F2 key.
 	/// </summary>
 	/// </summary>
-	F2,
+	F2 = MaxCodePoint + ConsoleKey.F2,
+
 	/// <summary>
 	/// <summary>
 	/// F3 key.
 	/// F3 key.
 	/// </summary>
 	/// </summary>
-	F3,
+	F3 = MaxCodePoint + ConsoleKey.F3,
+
 	/// <summary>
 	/// <summary>
 	/// F4 key.
 	/// F4 key.
 	/// </summary>
 	/// </summary>
-	F4,
+	F4 = MaxCodePoint + ConsoleKey.F4,
+
 	/// <summary>
 	/// <summary>
 	/// F5 key.
 	/// F5 key.
 	/// </summary>
 	/// </summary>
-	F5,
+	F5 = MaxCodePoint + ConsoleKey.F5,
+
 	/// <summary>
 	/// <summary>
 	/// F6 key.
 	/// F6 key.
 	/// </summary>
 	/// </summary>
-	F6,
+	F6 = MaxCodePoint + ConsoleKey.F6,
+
 	/// <summary>
 	/// <summary>
 	/// F7 key.
 	/// F7 key.
 	/// </summary>
 	/// </summary>
-	F7,
+	F7 = MaxCodePoint + ConsoleKey.F7,
+
 	/// <summary>
 	/// <summary>
 	/// F8 key.
 	/// F8 key.
 	/// </summary>
 	/// </summary>
-	F8,
+	F8 = MaxCodePoint + ConsoleKey.F8,
+
 	/// <summary>
 	/// <summary>
 	/// F9 key.
 	/// F9 key.
 	/// </summary>
 	/// </summary>
-	F9,
+	F9 = MaxCodePoint + ConsoleKey.F9,
+
 	/// <summary>
 	/// <summary>
 	/// F10 key.
 	/// F10 key.
 	/// </summary>
 	/// </summary>
-	F10,
+	F10 = MaxCodePoint + ConsoleKey.F10,
+
 	/// <summary>
 	/// <summary>
 	/// F11 key.
 	/// F11 key.
 	/// </summary>
 	/// </summary>
-	F11,
+	F11 = MaxCodePoint + ConsoleKey.F11,
+
 	/// <summary>
 	/// <summary>
 	/// F12 key.
 	/// F12 key.
 	/// </summary>
 	/// </summary>
-	F12,
+	F12 = MaxCodePoint + ConsoleKey.F12,
+
 	/// <summary>
 	/// <summary>
 	/// F13 key.
 	/// F13 key.
 	/// </summary>
 	/// </summary>
-	F13,
+	F13 = MaxCodePoint + ConsoleKey.F13,
+
 	/// <summary>
 	/// <summary>
 	/// F14 key.
 	/// F14 key.
 	/// </summary>
 	/// </summary>
-	F14,
+	F14 = MaxCodePoint + ConsoleKey.F14,
+
 	/// <summary>
 	/// <summary>
 	/// F15 key.
 	/// F15 key.
 	/// </summary>
 	/// </summary>
-	F15,
+	F15 = MaxCodePoint + ConsoleKey.F15,
+
 	/// <summary>
 	/// <summary>
 	/// F16 key.
 	/// F16 key.
 	/// </summary>
 	/// </summary>
-	F16,
+	F16 = MaxCodePoint + ConsoleKey.F16,
+
 	/// <summary>
 	/// <summary>
 	/// F17 key.
 	/// F17 key.
 	/// </summary>
 	/// </summary>
-	F17,
+	F17 = MaxCodePoint + ConsoleKey.F17,
+
 	/// <summary>
 	/// <summary>
 	/// F18 key.
 	/// F18 key.
 	/// </summary>
 	/// </summary>
-	F18,
+	F18 = MaxCodePoint + ConsoleKey.F18,
+
 	/// <summary>
 	/// <summary>
 	/// F19 key.
 	/// F19 key.
 	/// </summary>
 	/// </summary>
-	F19,
+	F19 = MaxCodePoint + ConsoleKey.F19,
+
 	/// <summary>
 	/// <summary>
 	/// F20 key.
 	/// F20 key.
 	/// </summary>
 	/// </summary>
-	F20,
+	F20 = MaxCodePoint + ConsoleKey.F20,
+
 	/// <summary>
 	/// <summary>
 	/// F21 key.
 	/// F21 key.
 	/// </summary>
 	/// </summary>
-	F21,
+	F21 = MaxCodePoint + ConsoleKey.F21,
+
 	/// <summary>
 	/// <summary>
 	/// F22 key.
 	/// F22 key.
 	/// </summary>
 	/// </summary>
-	F22,
+	F22 = MaxCodePoint + ConsoleKey.F22,
+
 	/// <summary>
 	/// <summary>
 	/// F23 key.
 	/// F23 key.
 	/// </summary>
 	/// </summary>
-	F23,
+	F23 = MaxCodePoint + ConsoleKey.F23,
+
 	/// <summary>
 	/// <summary>
 	/// F24 key.
 	/// F24 key.
 	/// </summary>
 	/// </summary>
-	F24,
-}
-
+	F24 = MaxCodePoint + ConsoleKey.F24,
+}

+ 1665 - 541
Terminal.Gui/ConsoleDrivers/ConsoleKeyMapping.cs

@@ -2,597 +2,1721 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
 using System.Linq;
 using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
 
 
-namespace Terminal.Gui.ConsoleDrivers {
+namespace Terminal.Gui.ConsoleDrivers;
+
+/// <summary>
+/// Helper class to handle the scan code and virtual key from a <see cref="ConsoleKey"/>.
+/// </summary>
+public static class ConsoleKeyMapping {
+
+#if !WT_ISSUE_8871_FIXED // https://github.com/microsoft/terminal/issues/8871
 	/// <summary>
 	/// <summary>
-	/// Helper class to handle the scan code and virtual key from a <see cref="ConsoleKey"/>.
+	/// Translates (maps) a virtual-key code into a scan code or character value, or translates a scan code into a virtual-key code.
 	/// </summary>
 	/// </summary>
-	public static class ConsoleKeyMapping {
-		class ScanCodeMapping : IEquatable<ScanCodeMapping> {
-			public uint ScanCode;
-			public uint VirtualKey;
-			public ConsoleModifiers Modifiers;
-			public uint UnicodeChar;
-
-			public ScanCodeMapping (uint scanCode, uint virtualKey, ConsoleModifiers modifiers, uint unicodeChar)
-			{
-				ScanCode = scanCode;
-				VirtualKey = virtualKey;
-				Modifiers = modifiers;
-				UnicodeChar = unicodeChar;
-			}
+	/// <param name="vk"></param>
+	/// <param name="uMapType">
+	/// If MAPVK_VK_TO_CHAR (2) - The uCode parameter is a virtual-key code and is translated into an un-shifted
+	/// character value in the low order word of the return value. 
+	/// </param>
+	/// <param name="dwhkl"></param>
+	/// <returns>An un-shifted character value in the low order word of the return value. Dead keys (diacritics)
+	/// are indicated by setting the top bit of the return value. If there is no translation,
+	/// the function returns 0. See Remarks.</returns>
+	[DllImport ("user32.dll", EntryPoint = "MapVirtualKeyExW", CharSet = CharSet.Unicode)]
+	extern static uint MapVirtualKeyEx (VK vk, uint uMapType, IntPtr dwhkl);
 
 
-			public bool Equals (ScanCodeMapping other)
-			{
-				return ScanCode.Equals (other.ScanCode) &&
-					VirtualKey.Equals (other.VirtualKey) &&
-					Modifiers.Equals (other.Modifiers) &&
-					UnicodeChar.Equals (other.UnicodeChar);
-			}
-		}
+	/// <summary>
+	/// Retrieves the active input locale identifier (formerly called the keyboard layout).
+	/// </summary>
+	/// <param name="idThread">0 for current thread</param>
+	/// <returns>The return value is the input locale identifier for the thread.
+	/// The low word contains a Language Identifier for the input language
+	/// and the high word contains a device handle to the physical layout of the keyboard.
+	/// </returns>
+	[DllImport ("user32.dll", EntryPoint = "GetKeyboardLayout", CharSet = CharSet.Unicode)]
+	extern static IntPtr GetKeyboardLayout (IntPtr idThread);
 
 
-		static ConsoleModifiers GetModifiers (ConsoleModifiers modifiers)
-		{
-			if (modifiers.HasFlag (ConsoleModifiers.Shift)
-			&& !modifiers.HasFlag (ConsoleModifiers.Alt)
-			&& !modifiers.HasFlag (ConsoleModifiers.Control)) {
-				return ConsoleModifiers.Shift;
-			} else if (modifiers == (ConsoleModifiers.Alt | ConsoleModifiers.Control)) {
-				return modifiers;
-			}
+	//[DllImport ("user32.dll", EntryPoint = "GetKeyboardLayoutNameW", CharSet = CharSet.Unicode)]
+	//extern static uint GetKeyboardLayoutName (uint idThread);
+
+	[DllImport ("user32.dll")]
+	extern static IntPtr GetForegroundWindow ();
 
 
+	[DllImport ("user32.dll")]
+	extern static IntPtr GetWindowThreadProcessId (IntPtr hWnd, IntPtr ProcessId);
+
+	/// <summary>
+	/// Translates the specified virtual-key code and keyboard state to the corresponding Unicode character or characters using
+	/// the Win32 API MapVirtualKey.
+	/// </summary>
+	/// <param name="vk"></param>
+	/// <returns>An un-shifted character value in the low order word of the return value. Dead keys (diacritics)
+	/// are indicated by setting the top bit of the return value. If there is no translation,
+	/// the function returns 0.</returns>
+	public static uint MapVKtoChar (VK vk)
+	{
+		if (Environment.OSVersion.Platform != PlatformID.Win32NT) {
 			return 0;
 			return 0;
 		}
 		}
+		var tid = GetWindowThreadProcessId (GetForegroundWindow (), 0);
+		var hkl = GetKeyboardLayout (tid);
+		return MapVirtualKeyEx (vk, 2, hkl);
+	}
+#else
+	/// <summary>
+	/// Translates (maps) a virtual-key code into a scan code or character value, or translates a scan code into a virtual-key code.
+	/// </summary>
+	/// <param name="vk"></param>
+	/// <param name="uMapType">
+	/// If MAPVK_VK_TO_CHAR (2) - The uCode parameter is a virtual-key code and is translated into an unshifted
+	/// character value in the low order word of the return value. 
+	/// </param>
+	/// <returns>An unshifted character value in the low order word of the return value. Dead keys (diacritics)
+	/// are indicated by setting the top bit of the return value. If there is no translation,
+	/// the function returns 0. See Remarks.</returns>
+	[DllImport ("user32.dll", EntryPoint = "MapVirtualKeyW", CharSet = CharSet.Unicode)]
+	extern static uint MapVirtualKey (VK vk, uint uMapType = 2);
+
+	uint MapVKtoChar (VK vk) => MapVirtualKeyToCharEx (vk);
+#endif
+	/// <summary>
+	/// Retrieves the name of the active input locale identifier (formerly called the keyboard layout) for the calling thread.
+	/// </summary>
+	/// <param name="pwszKLID"></param>
+	/// <returns></returns>
+	[DllImport ("user32.dll")]
+	extern static bool GetKeyboardLayoutName ([Out] StringBuilder pwszKLID);
+
+	public static string GetKeyboardLayoutName ()
+	{
+		if (Environment.OSVersion.Platform != PlatformID.Win32NT) {
+			return "none";
+		}
 
 
-		static ScanCodeMapping GetScanCode (string propName, uint keyValue, ConsoleModifiers modifiers)
-		{
-			switch (propName) {
-			case "UnicodeChar":
-				var sCode = scanCodes.FirstOrDefault ((e) => e.UnicodeChar == keyValue && e.Modifiers == modifiers);
-				if (sCode == null && modifiers == (ConsoleModifiers.Alt | ConsoleModifiers.Control)) {
-					return scanCodes.FirstOrDefault ((e) => e.UnicodeChar == keyValue && e.Modifiers == 0);
-				}
-				return sCode;
-			case "VirtualKey":
-				sCode = scanCodes.FirstOrDefault ((e) => e.VirtualKey == keyValue && e.Modifiers == modifiers);
-				if (sCode == null && modifiers == (ConsoleModifiers.Alt | ConsoleModifiers.Control)) {
-					return scanCodes.FirstOrDefault ((e) => e.VirtualKey == keyValue && e.Modifiers == 0);
-				}
-				return sCode;
-			}
+		StringBuilder klidSB = new StringBuilder ();
+		GetKeyboardLayoutName (klidSB);
+		return klidSB.ToString ();
+	}
 
 
-			return null;
+	class ScanCodeMapping : IEquatable<ScanCodeMapping> {
+		public uint ScanCode;
+		public VK VirtualKey;
+		public ConsoleModifiers Modifiers;
+		public uint UnicodeChar;
+
+		public ScanCodeMapping (uint scanCode, VK virtualKey, ConsoleModifiers modifiers, uint unicodeChar)
+		{
+			ScanCode = scanCode;
+			VirtualKey = virtualKey;
+			Modifiers = modifiers;
+			UnicodeChar = unicodeChar;
 		}
 		}
 
 
-		/// <summary>
-		/// Gets the <see cref="ConsoleKey"/> from the provided <see cref="KeyCode"/>.
-		/// </summary>
-		/// <param name="key"></param>
-		/// <returns></returns>
-		public static ConsoleKeyInfo GetConsoleKeyFromKey (KeyCode key)
+		public bool Equals (ScanCodeMapping other)
 		{
 		{
-			var mod = new ConsoleModifiers ();
-			if (key.HasFlag (KeyCode.ShiftMask)) {
-				mod |= ConsoleModifiers.Shift;
-			}
-			if (key.HasFlag (KeyCode.AltMask)) {
-				mod |= ConsoleModifiers.Alt;
+			return ScanCode.Equals (other.ScanCode) &&
+				VirtualKey.Equals (other.VirtualKey) &&
+				Modifiers.Equals (other.Modifiers) &&
+				UnicodeChar.Equals (other.UnicodeChar);
+		}
+	}
+
+	static ConsoleModifiers GetModifiers (ConsoleModifiers modifiers)
+	{
+		if (modifiers.HasFlag (ConsoleModifiers.Shift)
+		&& !modifiers.HasFlag (ConsoleModifiers.Alt)
+		&& !modifiers.HasFlag (ConsoleModifiers.Control)) {
+			return ConsoleModifiers.Shift;
+		} else if (modifiers == (ConsoleModifiers.Alt | ConsoleModifiers.Control)) {
+			return modifiers;
+		}
+
+		return 0;
+	}
+
+	static ScanCodeMapping GetScanCode (string propName, uint keyValue, ConsoleModifiers modifiers)
+	{
+		switch (propName) {
+		case "UnicodeChar":
+			var sCode = _scanCodes.FirstOrDefault ((e) => e.UnicodeChar == keyValue && e.Modifiers == modifiers);
+			if (sCode == null && modifiers == (ConsoleModifiers.Alt | ConsoleModifiers.Control)) {
+				return _scanCodes.FirstOrDefault ((e) => e.UnicodeChar == keyValue && e.Modifiers == 0);
 			}
 			}
-			if (key.HasFlag (KeyCode.CtrlMask)) {
-				mod |= ConsoleModifiers.Control;
+			return sCode;
+		case "VirtualKey":
+			sCode = _scanCodes.FirstOrDefault ((e) => e.VirtualKey == (VK)keyValue && e.Modifiers == modifiers);
+			if (sCode == null && modifiers == (ConsoleModifiers.Alt | ConsoleModifiers.Control)) {
+				return _scanCodes.FirstOrDefault ((e) => e.VirtualKey == (VK)keyValue && e.Modifiers == 0);
 			}
 			}
-			return GetConsoleKeyFromKey ((uint)(key & ~KeyCode.CtrlMask & ~KeyCode.ShiftMask & ~KeyCode.AltMask), mod, out _);
+			return sCode;
 		}
 		}
 
 
-		/// <summary>
-		/// Get the <see cref="ConsoleKeyInfo"/> from a unicode character and modifiers (e.g. (Key)'a' and (Key)Key.CtrlMask).
-		/// </summary>
-		/// <param name="keyValue">The key as a unicode codepoint.</param>
-		/// <param name="modifiers">The modifier keys.</param>
-		/// <param name="scanCode">The resulting scan code.</param>
-		/// <returns>The <see cref="ConsoleKeyInfo"/>.</returns>
-		public static ConsoleKeyInfo GetConsoleKeyFromKey (uint keyValue, ConsoleModifiers modifiers, out uint scanCode)
-		{
-			scanCode = 0;
-			uint outputChar = keyValue;
-			if (keyValue == 0) {
-				return new ConsoleKeyInfo ((char)keyValue, ConsoleKey.None, modifiers.HasFlag (ConsoleModifiers.Shift),
+		return null;
+	}
+
+	// BUGBUG: This API is not correct. It is only used by WindowsDriver in VKPacket scenarios
+	/// <summary>
+	/// Get the scan code from a <see cref="ConsoleKeyInfo"/>.
+	/// </summary>
+	/// <param name="consoleKeyInfo">The console key info.</param>
+	/// <returns>The value if apply.</returns>
+	public static uint GetScanCodeFromConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
+	{
+		var mod = GetModifiers (consoleKeyInfo.Modifiers);
+		ScanCodeMapping scode = GetScanCode ("VirtualKey", (uint)consoleKeyInfo.Key, mod);
+		if (scode != null) {
+			return scode.ScanCode;
+		}
+
+		return 0;
+	}
+
+	// BUGBUG: This API is not correct. It is only used by FakeDriver and VkeyPacketSimulator
+	/// <summary>
+	/// Gets the <see cref="ConsoleKeyInfo"/> from the provided <see cref="KeyCode"/>.
+	/// </summary>
+	/// <param name="key">The key code.</param>
+	/// <returns>The console key info.</returns>
+	public static ConsoleKeyInfo GetConsoleKeyInfoFromKeyCode (KeyCode key)
+	{
+		var modifiers = MapToConsoleModifiers (key);
+		var keyValue = MapKeyCodeToConsoleKey (key, out bool isConsoleKey);
+		if (isConsoleKey) {
+			var mod = GetModifiers (modifiers);
+			var scode = GetScanCode ("VirtualKey", (uint)keyValue, mod);
+			if (scode != null) {
+				return new ConsoleKeyInfo ((char)scode.UnicodeChar, (ConsoleKey)scode.VirtualKey, modifiers.HasFlag (ConsoleModifiers.Shift),
+					modifiers.HasFlag (ConsoleModifiers.Alt), modifiers.HasFlag (ConsoleModifiers.Control));
+			}
+		} else {
+			var keyChar = GetKeyCharFromUnicodeChar ((uint)keyValue, modifiers, out uint consoleKey, out _, isConsoleKey);
+			if (consoleKey != 0) {
+				return new ConsoleKeyInfo ((char)keyChar, (ConsoleKey)consoleKey, modifiers.HasFlag (ConsoleModifiers.Shift),
 					modifiers.HasFlag (ConsoleModifiers.Alt), modifiers.HasFlag (ConsoleModifiers.Control));
 					modifiers.HasFlag (ConsoleModifiers.Alt), modifiers.HasFlag (ConsoleModifiers.Control));
 			}
 			}
+		}
+
+		return new ConsoleKeyInfo ((char)keyValue, ConsoleKey.None, modifiers.HasFlag (ConsoleModifiers.Shift),
+			modifiers.HasFlag (ConsoleModifiers.Alt), modifiers.HasFlag (ConsoleModifiers.Control));
+	}
+
+	/// <summary>
+	/// Map existing <see cref="KeyCode"/> modifiers to <see cref="ConsoleModifiers"/>.
+	/// </summary>
+	/// <param name="key">The key code.</param>
+	/// <returns>The console modifiers.</returns>
+	public static ConsoleModifiers MapToConsoleModifiers (KeyCode key)
+	{
+		var modifiers = new ConsoleModifiers ();
+		if (key.HasFlag (KeyCode.ShiftMask)) {
+			modifiers |= ConsoleModifiers.Shift;
+		}
+		if (key.HasFlag (KeyCode.AltMask)) {
+			modifiers |= ConsoleModifiers.Alt;
+		}
+		if (key.HasFlag (KeyCode.CtrlMask)) {
+			modifiers |= ConsoleModifiers.Control;
+		}
 
 
-			uint consoleKey = (uint)MapKeyToConsoleKey ((KeyCode)keyValue, modifiers, out bool mappable);
-			if (mappable) {
-				var mod = GetModifiers (modifiers);
-				var scode = GetScanCode ("UnicodeChar", keyValue, mod);
-				if (scode != null) {
-					consoleKey = scode.VirtualKey;
-					scanCode = scode.ScanCode;
-					outputChar = scode.UnicodeChar;
+		return modifiers;
+	}
+
+	/// <summary>
+	/// Gets <see cref="ConsoleModifiers"/> from <see cref="bool"/> modifiers.
+	/// </summary>
+	/// <param name="shift">The shift key.</param>
+	/// <param name="alt">The alt key.</param>
+	/// <param name="control">The control key.</param>
+	/// <returns>The console modifiers.</returns>
+	public static ConsoleModifiers GetModifiers (bool shift, bool alt, bool control)
+	{
+		var modifiers = new ConsoleModifiers ();
+		if (shift) {
+			modifiers |= ConsoleModifiers.Shift;
+		}
+		if (alt) {
+			modifiers |= ConsoleModifiers.Alt;
+		}
+		if (control) {
+			modifiers |= ConsoleModifiers.Control;
+		}
+
+		return modifiers;
+	}
+
+	/// <summary>
+	/// Get the <see cref="ConsoleKeyInfo"/> from a unicode character and modifiers (e.g. (Key)'a' and (Key)Key.CtrlMask).
+	/// </summary>
+	/// <param name="keyValue">The key as a unicode codepoint.</param>
+	/// <param name="modifiers">The modifier keys.</param>
+	/// <param name="scanCode">The resulting scan code.</param>
+	/// <returns>The <see cref="ConsoleKeyInfo"/>.</returns>
+	static ConsoleKeyInfo GetConsoleKeyInfoFromKeyChar (uint keyValue, ConsoleModifiers modifiers, out uint scanCode)
+	{
+		scanCode = 0;
+		if (keyValue == 0) {
+			return new ConsoleKeyInfo ((char)keyValue, ConsoleKey.None, modifiers.HasFlag (ConsoleModifiers.Shift),
+				modifiers.HasFlag (ConsoleModifiers.Alt), modifiers.HasFlag (ConsoleModifiers.Control));
+		}
+
+		uint outputChar = keyValue;
+		uint consoleKey;
+		if (keyValue > byte.MaxValue) {
+			var sCode = _scanCodes.FirstOrDefault ((e) => e.UnicodeChar == keyValue);
+			if (sCode == null) {
+				consoleKey = (byte)(keyValue & byte.MaxValue);
+				sCode = _scanCodes.FirstOrDefault ((e) => e.VirtualKey == (VK)consoleKey);
+				if (sCode == null) {
+					consoleKey = 0;
+					outputChar = keyValue;
 				} else {
 				} else {
-					// If the consoleKey is < 255, retain the lower 8 bits of the key value and set the upper bits to 0xff.
-					// This is a shifted value that will be used by the GetKeyCharFromConsoleKey to do the correct action
-					// because keyValue maybe a UnicodeChar or a ConsoleKey, e.g. for PageUp is passed the ConsoleKey.PageUp
-					consoleKey = consoleKey < 0xff ? consoleKey & 0xff | 0xff << 8 : consoleKey;
-					outputChar = GetKeyCharFromConsoleKey (consoleKey, modifiers, out consoleKey, out scanCode);
+					outputChar = (char)(keyValue >> 8);
 				}
 				}
 			} else {
 			} else {
-				var mod = GetModifiers (modifiers);
-				var scode = GetScanCode ("VirtualKey", consoleKey, mod);
-				if (scode != null) {
-					consoleKey = scode.VirtualKey;
-					scanCode = scode.ScanCode;
-					outputChar = scode.UnicodeChar;
-				}
+				consoleKey = (byte)sCode.VirtualKey;
+				outputChar = keyValue;
 			}
 			}
+		} else {
+			consoleKey = (byte)keyValue;
+			outputChar = '\0';
+		}
 
 
-			return new ConsoleKeyInfo ((char)outputChar, (ConsoleKey)consoleKey, modifiers.HasFlag (ConsoleModifiers.Shift),
-					modifiers.HasFlag (ConsoleModifiers.Alt), modifiers.HasFlag (ConsoleModifiers.Control));
+		return new ConsoleKeyInfo ((char)outputChar, (ConsoleKey)consoleKey, modifiers.HasFlag (ConsoleModifiers.Shift),
+			modifiers.HasFlag (ConsoleModifiers.Alt), modifiers.HasFlag (ConsoleModifiers.Control));
+	}
+
+	// Used only by unit tests
+	internal static uint GetKeyChar (uint keyValue, ConsoleModifiers modifiers)
+	{
+		if (modifiers == ConsoleModifiers.Shift && keyValue - 32 is >= 'A' and <= 'Z') {
+			return keyValue - 32;
+		} else if (modifiers == ConsoleModifiers.None && keyValue is >= 'A' and <= 'Z') {
+			return keyValue + 32;
 		}
 		}
 
 
-		/// <summary>
-		/// Get the output character from the <see cref="ConsoleKey"/>, the correct <see cref="ConsoleKey"/>
-		/// and the scan code used on <see cref="WindowsDriver"/>.
-		/// </summary>
-		/// <param name="unicodeChar">The unicode character.</param>
-		/// <param name="modifiers">The modifiers keys.</param>
-		/// <param name="consoleKey">The resulting console key.</param>
-		/// <param name="scanCode">The resulting scan code.</param>
-		/// <returns>The output character or the <paramref name="consoleKey"/>.</returns>
-		static uint GetKeyCharFromConsoleKey (uint unicodeChar, ConsoleModifiers modifiers, out uint consoleKey, out uint scanCode)
-		{
-			uint decodedChar = unicodeChar >> 8 == 0xff ? unicodeChar & 0xff : unicodeChar;
-			uint keyChar = decodedChar;
-			consoleKey = 0;
-			var mod = GetModifiers (modifiers);
-			scanCode = 0;
-			var scode = unicodeChar != 0 && unicodeChar >> 8 != 0xff ? GetScanCode ("VirtualKey", decodedChar, mod) : null;
+		if (modifiers == ConsoleModifiers.Shift && keyValue - 32 is >= 'À' and <= 'Ý') {
+			return keyValue - 32;
+		} else if (modifiers == ConsoleModifiers.None && keyValue is >= 'À' and <= 'Ý') {
+			return keyValue + 32;
+		}
+
+		if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '0') {
+			return keyValue + 13;
+		} else if (modifiers == ConsoleModifiers.None && keyValue - 13 is '0') {
+			return keyValue - 13;
+		}
+
+		if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is >= '1' and <= '9' and not '7') {
+			return keyValue - 16;
+		} else if (modifiers == ConsoleModifiers.None && keyValue + 16 is >= '1' and <= '9' and not '7') {
+			return keyValue + 16;
+		}
+
+		if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '7') {
+			return keyValue - 8;
+		} else if (modifiers == ConsoleModifiers.None && keyValue + 8 is '7') {
+			return keyValue + 8;
+		}
+
+		if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '\'') {
+			return keyValue + 24;
+		} else if (modifiers == ConsoleModifiers.None && keyValue - 24 is '\'') {
+			return keyValue - 24;
+		}
+
+		if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '«') {
+			return keyValue + 16;
+		} else if (modifiers == ConsoleModifiers.None && keyValue - 16 is '«') {
+			return keyValue - 16;
+		}
+
+		if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '\\') {
+			return keyValue + 32;
+		} else if (modifiers == ConsoleModifiers.None && keyValue - 32 is '\\') {
+			return keyValue - 32;
+		}
+
+		if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '+') {
+			return keyValue - 1;
+		} else if (modifiers == ConsoleModifiers.None && keyValue + 1 is '+') {
+			return keyValue + 1;
+		}
+
+		if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '´') {
+			return keyValue - 84;
+		} else if (modifiers == ConsoleModifiers.None && keyValue + 84 is '´') {
+			return keyValue + 84;
+		}
+
+		if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is 'º') {
+			return keyValue - 16;
+		} else if (modifiers == ConsoleModifiers.None && keyValue + 16 is 'º') {
+			return keyValue + 16;
+		}
+
+		if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '~') {
+			return keyValue - 32;
+		} else if (modifiers == ConsoleModifiers.None && keyValue + 32 is '~') {
+			return keyValue + 32;
+		}
+
+		if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '<') {
+			return keyValue + 2;
+		} else if (modifiers == ConsoleModifiers.None && keyValue - 2 is '<') {
+			return keyValue - 2;
+		}
+
+		if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is ',') {
+			return keyValue + 15;
+		} else if (modifiers == ConsoleModifiers.None && keyValue - 15 is ',') {
+			return keyValue - 15;
+		}
+		if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '.') {
+			return keyValue + 12;
+		} else if (modifiers == ConsoleModifiers.None && keyValue - 12 is '.') {
+			return keyValue - 12;
+		}
+
+		if (modifiers.HasFlag (ConsoleModifiers.Shift) && keyValue is '-') {
+			return keyValue + 50;
+		} else if (modifiers == ConsoleModifiers.None && keyValue - 50 is '-') {
+			return keyValue - 50;
+		}
+
+		return keyValue;
+	}
+
+	/// <summary>
+	/// Get the output character from the <see cref="GetConsoleKeyInfoFromKeyCode"/>, with the correct <see cref="ConsoleKey"/>
+	/// and the scan code used on <see cref="WindowsDriver"/>.
+	/// </summary>
+	/// <param name="unicodeChar">The unicode character.</param>
+	/// <param name="modifiers">The modifiers keys.</param>
+	/// <param name="consoleKey">The resulting console key.</param>
+	/// <param name="scanCode">The resulting scan code.</param>
+	/// <param name="isConsoleKey">Indicates if the <paramref name="unicodeChar"/> is a <see cref="ConsoleKey"/>.</param>
+	/// <returns>The output character or the <paramref name="consoleKey"/>.</returns>
+	/// <remarks>This is only used by the <see cref="GetConsoleKeyInfoFromKeyCode"/> and by unit tests.</remarks>
+	internal static uint GetKeyCharFromUnicodeChar (uint unicodeChar, ConsoleModifiers modifiers, out uint consoleKey, out uint scanCode, bool isConsoleKey = false)
+	{
+		uint decodedChar = unicodeChar >> 8 == 0xff ? unicodeChar & 0xff : unicodeChar;
+		uint keyChar = decodedChar;
+		consoleKey = 0;
+		var mod = GetModifiers (modifiers);
+		scanCode = 0;
+		ScanCodeMapping scode = null;
+		if (unicodeChar != 0 && unicodeChar >> 8 != 0xff && isConsoleKey) {
+			scode = GetScanCode ("VirtualKey", decodedChar, mod);
+		}
+		if (isConsoleKey && scode != null) {
+			consoleKey = (uint)scode.VirtualKey;
+			keyChar = scode.UnicodeChar;
+			scanCode = scode.ScanCode;
+		}
+		if (scode == null) {
+			scode = unicodeChar != 0 ? GetScanCode ("UnicodeChar", decodedChar, mod) : null;
 			if (scode != null) {
 			if (scode != null) {
-				consoleKey = scode.VirtualKey;
+				consoleKey = (uint)scode.VirtualKey;
 				keyChar = scode.UnicodeChar;
 				keyChar = scode.UnicodeChar;
 				scanCode = scode.ScanCode;
 				scanCode = scode.ScanCode;
 			}
 			}
-			if (scode == null) {
-				scode = unicodeChar != 0 ? GetScanCode ("UnicodeChar", decodedChar, mod) : null;
-				if (scode != null) {
-					consoleKey = scode.VirtualKey;
-					keyChar = scode.UnicodeChar;
-					scanCode = scode.ScanCode;
-				}
-			}
-			if (decodedChar != 0 && scanCode == 0 && char.IsLetter ((char)decodedChar)) {
-				string stFormD = ((char)decodedChar).ToString ().Normalize (System.Text.NormalizationForm.FormD);
-				for (int i = 0; i < stFormD.Length; i++) {
-					var uc = CharUnicodeInfo.GetUnicodeCategory (stFormD [i]);
-					if (uc != UnicodeCategory.NonSpacingMark && uc != UnicodeCategory.OtherLetter) {
-						consoleKey = char.ToUpper (stFormD [i]);
-						scode = GetScanCode ("VirtualKey", char.ToUpper (stFormD [i]), 0);
-						if (scode != null) {
-							scanCode = scode.ScanCode;
-						}
+		}
+		if (decodedChar != 0 && scanCode == 0 && char.IsLetter ((char)decodedChar)) {
+			string stFormD = ((char)decodedChar).ToString ().Normalize (System.Text.NormalizationForm.FormD);
+			for (int i = 0; i < stFormD.Length; i++) {
+				var uc = CharUnicodeInfo.GetUnicodeCategory (stFormD [i]);
+				if (uc != UnicodeCategory.NonSpacingMark && uc != UnicodeCategory.OtherLetter) {
+					consoleKey = char.ToUpper (stFormD [i]);
+					scode = GetScanCode ("VirtualKey", char.ToUpper (stFormD [i]), 0);
+					if (scode != null) {
+						scanCode = scode.ScanCode;
 					}
 					}
 				}
 				}
 			}
 			}
+		}
+		if (keyChar < 255 && consoleKey == 0 && scanCode == 0) {
+			scode = GetScanCode ("VirtualKey", keyChar, mod);
+			if (scode != null) {
+				consoleKey = (uint)scode.VirtualKey;
+				keyChar = scode.UnicodeChar;
+				scanCode = scode.ScanCode;
+			}
+		}
+
+		return keyChar;
+	}
+
+	/// <summary>
+	/// Maps a unicode character (e.g. (Key)'a') to a uint representing a <see cref="ConsoleKey"/>.
+	/// </summary>
+	/// <param name="keyValue">The key value.</param>
+	/// <param name="isConsoleKey">Indicates if the <paramref name="keyValue"/> is a <see cref="ConsoleKey"/>.
+	/// <see langword="true"/> means the return value is in the ConsoleKey enum.
+	/// <see langword="false"/> means the return value can be mapped to a valid unicode character.
+	/// </param>
+	/// <returns>The <see cref="ConsoleKey"/> or the <paramref name="keyValue"/>.</returns>
+	/// <remarks>This is only used by the <see cref="GetConsoleKeyInfoFromKeyCode"/> and by unit tests.</remarks>
+	internal static uint MapKeyCodeToConsoleKey (KeyCode keyValue, out bool isConsoleKey)
+	{
+		isConsoleKey = true;
+		keyValue = keyValue & ~KeyCode.CtrlMask & ~KeyCode.ShiftMask & ~KeyCode.AltMask;
+
+		switch (keyValue) {
+		case KeyCode.Enter:
+			return (uint)ConsoleKey.Enter;
+		case KeyCode.CursorUp:
+			return (uint)ConsoleKey.UpArrow;
+		case KeyCode.CursorDown:
+			return (uint)ConsoleKey.DownArrow;
+		case KeyCode.CursorLeft:
+			return (uint)ConsoleKey.LeftArrow;
+		case KeyCode.CursorRight:
+			return (uint)ConsoleKey.RightArrow;
+		case KeyCode.PageUp:
+			return (uint)ConsoleKey.PageUp;
+		case KeyCode.PageDown:
+			return (uint)ConsoleKey.PageDown;
+		case KeyCode.Home:
+			return (uint)ConsoleKey.Home;
+		case KeyCode.End:
+			return (uint)ConsoleKey.End;
+		case KeyCode.Insert:
+			return (uint)ConsoleKey.Insert;
+		case KeyCode.Delete:
+			return (uint)ConsoleKey.Delete;
+		case KeyCode.F1:
+			return (uint)ConsoleKey.F1;
+		case KeyCode.F2:
+			return (uint)ConsoleKey.F2;
+		case KeyCode.F3:
+			return (uint)ConsoleKey.F3;
+		case KeyCode.F4:
+			return (uint)ConsoleKey.F4;
+		case KeyCode.F5:
+			return (uint)ConsoleKey.F5;
+		case KeyCode.F6:
+			return (uint)ConsoleKey.F6;
+		case KeyCode.F7:
+			return (uint)ConsoleKey.F7;
+		case KeyCode.F8:
+			return (uint)ConsoleKey.F8;
+		case KeyCode.F9:
+			return (uint)ConsoleKey.F9;
+		case KeyCode.F10:
+			return (uint)ConsoleKey.F10;
+		case KeyCode.F11:
+			return (uint)ConsoleKey.F11;
+		case KeyCode.F12:
+			return (uint)ConsoleKey.F12;
+		case KeyCode.F13:
+			return (uint)ConsoleKey.F13;
+		case KeyCode.F14:
+			return (uint)ConsoleKey.F14;
+		case KeyCode.F15:
+			return (uint)ConsoleKey.F15;
+		case KeyCode.F16:
+			return (uint)ConsoleKey.F16;
+		case KeyCode.F17:
+			return (uint)ConsoleKey.F17;
+		case KeyCode.F18:
+			return (uint)ConsoleKey.F18;
+		case KeyCode.F19:
+			return (uint)ConsoleKey.F19;
+		case KeyCode.F20:
+			return (uint)ConsoleKey.F20;
+		case KeyCode.F21:
+			return (uint)ConsoleKey.F21;
+		case KeyCode.F22:
+			return (uint)ConsoleKey.F22;
+		case KeyCode.F23:
+			return (uint)ConsoleKey.F23;
+		case KeyCode.F24:
+			return (uint)ConsoleKey.F24;
+		case KeyCode.Tab | KeyCode.ShiftMask:
+			return (uint)ConsoleKey.Tab;
+		}
+
+		isConsoleKey = false;
+		return (uint)keyValue;
+	}
+
+	/// <summary>
+	/// Maps a <see cref="ConsoleKeyInfo"/> to a <see cref="KeyCode"/>.
+	/// </summary>
+	/// <param name="consoleKeyInfo">The console key.</param>
+	/// <returns>The <see cref="KeyCode"/> or the <paramref name="consoleKeyInfo"/>.</returns>
+	public static KeyCode MapConsoleKeyInfoToKeyCode (ConsoleKeyInfo consoleKeyInfo)
+	{
+		KeyCode keyCode;
+
+		switch (consoleKeyInfo.Key) {
+		case ConsoleKey.Enter:
+			keyCode = KeyCode.Enter;
+			break;
+		case ConsoleKey.Delete:
+			keyCode = KeyCode.Delete;
+			break;
+		case ConsoleKey.UpArrow:
+			keyCode = KeyCode.CursorUp;
+			break;
+		case ConsoleKey.DownArrow:
+			keyCode = KeyCode.CursorDown;
+			break;
+		case ConsoleKey.LeftArrow:
+			keyCode = KeyCode.CursorLeft;
+			break;
+		case ConsoleKey.RightArrow:
+			keyCode = KeyCode.CursorRight;
+			break;
+		case ConsoleKey.PageUp:
+			keyCode = KeyCode.PageUp;
+			break;
+		case ConsoleKey.PageDown:
+			keyCode = KeyCode.PageDown;
+			break;
+		case ConsoleKey.Home:
+			keyCode = KeyCode.Home;
+			break;
+		case ConsoleKey.End:
+			keyCode = KeyCode.End;
+			break;
+		case ConsoleKey.Insert:
+			keyCode = KeyCode.Insert;
+			break;
+		case ConsoleKey.F1:
+			keyCode = KeyCode.F1;
+			break;
+		case ConsoleKey.F2:
+			keyCode = KeyCode.F2;
+			break;
+		case ConsoleKey.F3:
+			keyCode = KeyCode.F3;
+			break;
+		case ConsoleKey.F4:
+			keyCode = KeyCode.F4;
+			break;
+		case ConsoleKey.F5:
+			keyCode = KeyCode.F5;
+			break;
+		case ConsoleKey.F6:
+			keyCode = KeyCode.F6;
+			break;
+		case ConsoleKey.F7:
+			keyCode = KeyCode.F7;
+			break;
+		case ConsoleKey.F8:
+			keyCode = KeyCode.F8;
+			break;
+		case ConsoleKey.F9:
+			keyCode = KeyCode.F9;
+			break;
+		case ConsoleKey.F10:
+			keyCode = KeyCode.F10;
+			break;
+		case ConsoleKey.F11:
+			keyCode = KeyCode.F11;
+			break;
+		case ConsoleKey.F12:
+			keyCode = KeyCode.F12;
+			break;
+		case ConsoleKey.F13:
+			keyCode = KeyCode.F13;
+			break;
+		case ConsoleKey.F14:
+			keyCode = KeyCode.F14;
+			break;
+		case ConsoleKey.F15:
+			keyCode = KeyCode.F15;
+			break;
+		case ConsoleKey.F16:
+			keyCode = KeyCode.F16;
+			break;
+		case ConsoleKey.F17:
+			keyCode = KeyCode.F17;
+			break;
+		case ConsoleKey.F18:
+			keyCode = KeyCode.F18;
+			break;
+		case ConsoleKey.F19:
+			keyCode = KeyCode.F19;
+			break;
+		case ConsoleKey.F20:
+			keyCode = KeyCode.F20;
+			break;
+		case ConsoleKey.F21:
+			keyCode = KeyCode.F21;
+			break;
+		case ConsoleKey.F22:
+			keyCode = KeyCode.F22;
+			break;
+		case ConsoleKey.F23:
+			keyCode = KeyCode.F23;
+			break;
+		case ConsoleKey.F24:
+			keyCode = KeyCode.F24;
+			break;
+		case ConsoleKey.Tab:
+			keyCode = KeyCode.Tab;
+			break;
+		default:
+			keyCode = (KeyCode)consoleKeyInfo.KeyChar;
+			break;
+		}
+		keyCode |= MapToKeyCodeModifiers (consoleKeyInfo.Modifiers, keyCode);
+
+		return keyCode;
+	}
 
 
-			return keyChar;
+	/// <summary>
+	/// Maps a <see cref="ConsoleKeyInfo"/> to a <see cref="KeyCode"/>.
+	/// </summary>
+	/// <param name="modifiers">The console modifiers.</param>
+	/// <param name="key">The key code.</param>
+	/// <returns>The <see cref="KeyCode"/> with <see cref="ConsoleModifiers"/> or the <paramref name="key"/></returns>
+	public static KeyCode MapToKeyCodeModifiers (ConsoleModifiers modifiers, KeyCode key)
+	{
+		var keyMod = new KeyCode ();
+		if ((modifiers & ConsoleModifiers.Shift) != 0) {
+			keyMod = KeyCode.ShiftMask;
+		}
+		if ((modifiers & ConsoleModifiers.Control) != 0) {
+			keyMod |= KeyCode.CtrlMask;
 		}
 		}
+		if ((modifiers & ConsoleModifiers.Alt) != 0) {
+			keyMod |= KeyCode.AltMask;
+		}
+
+		return keyMod != KeyCode.Null ? keyMod | key : key;
+	}
 
 
+	/// <summary>
+	/// Generated from winuser.h. See https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
+	/// </summary>
+	public enum VK : ushort {
 		/// <summary>
 		/// <summary>
-		/// Maps a unicode character (e.g. (Key)'a') to a uint representing a <see cref="ConsoleKey"/>.
+		/// Left mouse button.
 		/// </summary>
 		/// </summary>
-		/// <param name="keyValue">The key value.</param>
-		/// <param name="modifiers">The modifiers keys.</param>
-		/// <param name="isMappable">
-		/// <see langword="true"/> means the return value can be mapped to a valid unicode character.
-		/// <see langword="false"/> means the return value is in the ConsoleKey enum.
-		/// </param>
-		/// <returns>The <see cref="ConsoleKey"/> or the <paramref name="keyValue"/>.</returns>
-		public static ConsoleKey MapKeyToConsoleKey (KeyCode keyValue, ConsoleModifiers modifiers, out bool isMappable)
-		{
-			isMappable = false;
-
-			switch (keyValue) {
-			case KeyCode.Delete:
-				return ConsoleKey.Delete;
-			case KeyCode.CursorUp:
-				return ConsoleKey.UpArrow;
-			case KeyCode.CursorDown:
-				return ConsoleKey.DownArrow;
-			case KeyCode.CursorLeft:
-				return ConsoleKey.LeftArrow;
-			case KeyCode.CursorRight:
-				return ConsoleKey.RightArrow;
-			case KeyCode.PageUp:
-				return ConsoleKey.PageUp;
-			case KeyCode.PageDown:
-				return ConsoleKey.PageDown;
-			case KeyCode.Home:
-				return ConsoleKey.Home;
-			case KeyCode.End:
-				return ConsoleKey.End;
-			case KeyCode.InsertChar:
-				return ConsoleKey.Insert;
-			case KeyCode.DeleteChar:
-				return ConsoleKey.Delete;
-			case KeyCode.F1:
-				return ConsoleKey.F1;
-			case KeyCode.F2:
-				return ConsoleKey.F2;
-			case KeyCode.F3:
-				return ConsoleKey.F3;
-			case KeyCode.F4:
-				return ConsoleKey.F4;
-			case KeyCode.F5:
-				return ConsoleKey.F5;
-			case KeyCode.F6:
-				return ConsoleKey.F6;
-			case KeyCode.F7:
-				return ConsoleKey.F7;
-			case KeyCode.F8:
-				return ConsoleKey.F8;
-			case KeyCode.F9:
-				return ConsoleKey.F9;
-			case KeyCode.F10:
-				return ConsoleKey.F10;
-			case KeyCode.F11:
-				return ConsoleKey.F11;
-			case KeyCode.F12:
-				return ConsoleKey.F12;
-			case KeyCode.F13:
-				return ConsoleKey.F13;
-			case KeyCode.F14:
-				return ConsoleKey.F14;
-			case KeyCode.F15:
-				return ConsoleKey.F15;
-			case KeyCode.F16:
-				return ConsoleKey.F16;
-			case KeyCode.F17:
-				return ConsoleKey.F17;
-			case KeyCode.F18:
-				return ConsoleKey.F18;
-			case KeyCode.F19:
-				return ConsoleKey.F19;
-			case KeyCode.F20:
-				return ConsoleKey.F20;
-			case KeyCode.F21:
-				return ConsoleKey.F21;
-			case KeyCode.F22:
-				return ConsoleKey.F22;
-			case KeyCode.F23:
-				return ConsoleKey.F23;
-			case KeyCode.F24:
-				return ConsoleKey.F24;
-			case KeyCode.Tab | KeyCode.ShiftMask:
-				return ConsoleKey.Tab;
-			}
+		LBUTTON = 0x01,
 
 
-			isMappable = true;
+		/// <summary>
+		/// Right mouse button.
+		/// </summary>
+		RBUTTON = 0x02,
 
 
-			if (modifiers == ConsoleModifiers.Shift && keyValue - 32 is >= KeyCode.A and <= KeyCode.Z) {
-				return (ConsoleKey)(keyValue - 32);
-			} else if (modifiers == ConsoleModifiers.None && keyValue is >= KeyCode.A and <= KeyCode.Z) {
-				return (ConsoleKey)(keyValue + 32);
-			}
-			if (modifiers == ConsoleModifiers.Shift && keyValue - 32 is >= (KeyCode)'À' and <= (KeyCode)'Ý') {
-				return (ConsoleKey)(keyValue - 32);
-			} else if (modifiers == ConsoleModifiers.None && keyValue is >= (KeyCode)'À' and <= (KeyCode)'Ý') {
-				return (ConsoleKey)(keyValue + 32);
-			}
+		/// <summary>
+		/// Control-break processing.
+		/// </summary>
+		CANCEL = 0x03,
 
 
-			return (ConsoleKey)keyValue;
-		}
+		/// <summary>
+		/// Middle mouse button (three-button mouse).
+		/// </summary>
+		MBUTTON = 0x04,
 
 
 		/// <summary>
 		/// <summary>
-		/// Maps a <see cref="ConsoleKey"/> to a <see cref="KeyCode"/>.
+		/// X1 mouse button.
 		/// </summary>
 		/// </summary>
-		/// <param name="consoleKey">The console key.</param>
-		/// <param name="isMappable">If <see langword="true"/> is mapped to a valid character, otherwise <see langword="false"/>.</param>
-		/// <returns>The <see cref="KeyCode"/> or the <paramref name="consoleKey"/>.</returns>
-		public static KeyCode MapConsoleKeyToKey (ConsoleKey consoleKey, out bool isMappable)
-		{
-			isMappable = false;
-
-			switch (consoleKey) {
-			case ConsoleKey.Delete:
-				return KeyCode.Delete;
-			case ConsoleKey.UpArrow:
-				return KeyCode.CursorUp;
-			case ConsoleKey.DownArrow:
-				return KeyCode.CursorDown;
-			case ConsoleKey.LeftArrow:
-				return KeyCode.CursorLeft;
-			case ConsoleKey.RightArrow:
-				return KeyCode.CursorRight;
-			case ConsoleKey.PageUp:
-				return KeyCode.PageUp;
-			case ConsoleKey.PageDown:
-				return KeyCode.PageDown;
-			case ConsoleKey.Home:
-				return KeyCode.Home;
-			case ConsoleKey.End:
-				return KeyCode.End;
-			case ConsoleKey.Insert:
-				return KeyCode.InsertChar;
-			case ConsoleKey.F1:
-				return KeyCode.F1;
-			case ConsoleKey.F2:
-				return KeyCode.F2;
-			case ConsoleKey.F3:
-				return KeyCode.F3;
-			case ConsoleKey.F4:
-				return KeyCode.F4;
-			case ConsoleKey.F5:
-				return KeyCode.F5;
-			case ConsoleKey.F6:
-				return KeyCode.F6;
-			case ConsoleKey.F7:
-				return KeyCode.F7;
-			case ConsoleKey.F8:
-				return KeyCode.F8;
-			case ConsoleKey.F9:
-				return KeyCode.F9;
-			case ConsoleKey.F10:
-				return KeyCode.F10;
-			case ConsoleKey.F11:
-				return KeyCode.F11;
-			case ConsoleKey.F12:
-				return KeyCode.F12;
-			case ConsoleKey.F13:
-				return KeyCode.F13;
-			case ConsoleKey.F14:
-				return KeyCode.F14;
-			case ConsoleKey.F15:
-				return KeyCode.F15;
-			case ConsoleKey.F16:
-				return KeyCode.F16;
-			case ConsoleKey.F17:
-				return KeyCode.F17;
-			case ConsoleKey.F18:
-				return KeyCode.F18;
-			case ConsoleKey.F19:
-				return KeyCode.F19;
-			case ConsoleKey.F20:
-				return KeyCode.F20;
-			case ConsoleKey.F21:
-				return KeyCode.F21;
-			case ConsoleKey.F22:
-				return KeyCode.F22;
-			case ConsoleKey.F23:
-				return KeyCode.F23;
-			case ConsoleKey.F24:
-				return KeyCode.F24;
-			case ConsoleKey.Tab:
-				return KeyCode.Tab;
-			}
-			isMappable = true;
+		XBUTTON1 = 0x05,
 
 
-			if (consoleKey is >= ConsoleKey.A and <= ConsoleKey.Z) {
-				return (KeyCode)(consoleKey + 32);
-			}
+		/// <summary>
+		/// X2 mouse button.
+		/// </summary>
+		XBUTTON2 = 0x06,
 
 
-			return (KeyCode)consoleKey;
-		}
+		/// <summary>
+		/// BACKSPACE key.
+		/// </summary>
+		BACK = 0x08,
 
 
 		/// <summary>
 		/// <summary>
-		/// Maps a <see cref="ConsoleKeyInfo"/> to a <see cref="KeyCode"/>.
+		/// TAB key.
 		/// </summary>
 		/// </summary>
-		/// <param name="keyInfo">The console key info.</param>
-		/// <param name="key">The key.</param>
-		/// <returns>The <see cref="KeyCode"/> with <see cref="ConsoleModifiers"/> or the <paramref name="key"/></returns>
-		public static KeyCode MapKeyModifiers (ConsoleKeyInfo keyInfo, KeyCode key)
-		{
-			var keyMod = new KeyCode ();
-			if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) {
-				keyMod = KeyCode.ShiftMask;
-			}
-			if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) {
-				keyMod |= KeyCode.CtrlMask;
-			}
-			if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) {
-				keyMod |= KeyCode.AltMask;
-			}
+		TAB = 0x09,
 
 
-			return keyMod != KeyCode.Null ? keyMod | key : key;
-		}
-
-		static HashSet<ScanCodeMapping> scanCodes = new HashSet<ScanCodeMapping> {
-			new ScanCodeMapping (1, 27, 0, 27), // Escape
-			new ScanCodeMapping (1, 27, ConsoleModifiers.Shift, 27),
-			new ScanCodeMapping (2, 49, 0, 49), // D1
-			new ScanCodeMapping (2, 49, ConsoleModifiers.Shift, 33),
-			new ScanCodeMapping (3, 50, 0, 50), // D2
-			new ScanCodeMapping (3, 50, ConsoleModifiers.Shift, 34),
-			new ScanCodeMapping (3, 50, ConsoleModifiers.Alt | ConsoleModifiers.Control, 64),
-			new ScanCodeMapping (4, 51, 0, 51), // D3
-			new ScanCodeMapping (4, 51, ConsoleModifiers.Shift, 35),
-			new ScanCodeMapping (4, 51, ConsoleModifiers.Alt | ConsoleModifiers.Control, 163),
-			new ScanCodeMapping (5, 52, 0, 52), // D4
-			new ScanCodeMapping (5, 52, ConsoleModifiers.Shift, 36),
-			new ScanCodeMapping (5, 52, ConsoleModifiers.Alt | ConsoleModifiers.Control, 167),
-			new ScanCodeMapping (6, 53, 0, 53), // D5
-			new ScanCodeMapping (6, 53, ConsoleModifiers.Shift, 37),
-			new ScanCodeMapping (6, 53, ConsoleModifiers.Alt | ConsoleModifiers.Control, 8364),
-			new ScanCodeMapping (7, 54, 0, 54), // D6
-			new ScanCodeMapping (7, 54, ConsoleModifiers.Shift, 38),
-			new ScanCodeMapping (8, 55, 0, 55), // D7
-			new ScanCodeMapping (8, 55, ConsoleModifiers.Shift, 47),
-			new ScanCodeMapping (8, 55, ConsoleModifiers.Alt | ConsoleModifiers.Control, 123),
-			new ScanCodeMapping (9, 56, 0, 56), // D8
-			new ScanCodeMapping (9, 56, ConsoleModifiers.Shift, 40),
-			new ScanCodeMapping (9, 56, ConsoleModifiers.Alt | ConsoleModifiers.Control, 91),
-			new ScanCodeMapping (10, 57, 0, 57), // D9
-			new ScanCodeMapping (10, 57, ConsoleModifiers.Shift, 41),
-			new ScanCodeMapping (10, 57, ConsoleModifiers.Alt | ConsoleModifiers.Control, 93),
-			new ScanCodeMapping (11, 48, 0, 48), // D0
-			new ScanCodeMapping (11, 48, ConsoleModifiers.Shift, 61),
-			new ScanCodeMapping (11, 48, ConsoleModifiers.Alt | ConsoleModifiers.Control, 125),
-			new ScanCodeMapping (12, 219, 0, 39), // Oem4
-			new ScanCodeMapping (12, 219, ConsoleModifiers.Shift, 63),
-			new ScanCodeMapping (13, 221, 0, 171), // Oem6
-			new ScanCodeMapping (13, 221, ConsoleModifiers.Shift, 187),
-			new ScanCodeMapping (14, 8, 0, 8), // Backspace
-			new ScanCodeMapping (14, 8, ConsoleModifiers.Shift, 8),
-			new ScanCodeMapping (15, 9, 0, 9), // Tab
-			new ScanCodeMapping (15, 9, ConsoleModifiers.Shift, 15),
-			new ScanCodeMapping (16, 81, 0, 113), // Q
-			new ScanCodeMapping (16, 81, ConsoleModifiers.Shift, 81),
-			new ScanCodeMapping (17, 87, 0, 119), // W
-			new ScanCodeMapping (17, 87, ConsoleModifiers.Shift, 87),
-			new ScanCodeMapping (18, 69, 0, 101), // E
-			new ScanCodeMapping (18, 69, ConsoleModifiers.Shift, 69),
-			new ScanCodeMapping (19, 82, 0, 114), // R
-			new ScanCodeMapping (19, 82, ConsoleModifiers.Shift, 82),
-			new ScanCodeMapping (20, 84, 0, 116), // T
-			new ScanCodeMapping (20, 84, ConsoleModifiers.Shift, 84),
-			new ScanCodeMapping (21, 89, 0, 121), // Y
-			new ScanCodeMapping (21, 89, ConsoleModifiers.Shift, 89),
-			new ScanCodeMapping (22, 85, 0, 117), // U
-			new ScanCodeMapping (22, 85, ConsoleModifiers.Shift, 85),
-			new ScanCodeMapping (23, 73, 0, 105), // I
-			new ScanCodeMapping (23, 73, ConsoleModifiers.Shift, 73),
-			new ScanCodeMapping (24, 79, 0, 111), // O
-			new ScanCodeMapping (24, 79, ConsoleModifiers.Shift, 79),
-			new ScanCodeMapping (25, 80, 0, 112), // P
-			new ScanCodeMapping (25, 80, ConsoleModifiers.Shift, 80),
-			new ScanCodeMapping (26, 187, 0, 43), // OemPlus
-			new ScanCodeMapping (26, 187, ConsoleModifiers.Shift, 42),
-			new ScanCodeMapping (26, 187, ConsoleModifiers.Alt | ConsoleModifiers.Control, 168),
-			new ScanCodeMapping (27, 186, 0, 180), // Oem1
-			new ScanCodeMapping (27, 186, ConsoleModifiers.Shift, 96),
-			new ScanCodeMapping (28, 13, 0, 13), // Enter
-			new ScanCodeMapping (28, 13, ConsoleModifiers.Shift, 13),
-			new ScanCodeMapping (29, 17, 0, 0), // Control
-			new ScanCodeMapping (29, 17, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (scanCode: 30, virtualKey: 65, modifiers: 0, unicodeChar: 97), // VK = A, UC = 'a'
-			new ScanCodeMapping (30, 65, ConsoleModifiers.Shift, 65),  // VK = A | Shift, UC = 'A'
-			new ScanCodeMapping (31, 83, 0, 115), // S
-			new ScanCodeMapping (31, 83, ConsoleModifiers.Shift, 83),
-			new ScanCodeMapping (32, 68, 0, 100), // D
-			new ScanCodeMapping (32, 68, ConsoleModifiers.Shift, 68),
-			new ScanCodeMapping (33, 70, 0, 102), // F
-			new ScanCodeMapping (33, 70, ConsoleModifiers.Shift, 70),
-			new ScanCodeMapping (34, 71, 0, 103), // G
-			new ScanCodeMapping (34, 71, ConsoleModifiers.Shift, 71),
-			new ScanCodeMapping (35, 72, 0, 104), // H
-			new ScanCodeMapping (35, 72, ConsoleModifiers.Shift, 72),
-			new ScanCodeMapping (36, 74, 0, 106), // J
-			new ScanCodeMapping (36, 74, ConsoleModifiers.Shift, 74),
-			new ScanCodeMapping (37, 75, 0, 107), // K
-			new ScanCodeMapping (37, 75, ConsoleModifiers.Shift, 75),
-			new ScanCodeMapping (38, 76, 0, 108), // L
-			new ScanCodeMapping (38, 76, ConsoleModifiers.Shift, 76),
-			new ScanCodeMapping (39, 192, 0, 231), // Oem3
-			new ScanCodeMapping (39, 192, ConsoleModifiers.Shift, 199),
-			new ScanCodeMapping (40, 222, 0, 186), // Oem7
-			new ScanCodeMapping (40, 222, ConsoleModifiers.Shift, 170),
-			new ScanCodeMapping (41, 220, 0, 92), // Oem5
-			new ScanCodeMapping (41, 220, ConsoleModifiers.Shift, 124),
-			new ScanCodeMapping (42, 16, 0, 0), // LShift
-			new ScanCodeMapping (42, 16, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (43, 191, 0, 126), // Oem2
-			new ScanCodeMapping (43, 191, ConsoleModifiers.Shift, 94),
-			new ScanCodeMapping (44, 90, 0, 122), // Z
-			new ScanCodeMapping (44, 90, ConsoleModifiers.Shift, 90),
-			new ScanCodeMapping (45, 88, 0, 120), // X
-			new ScanCodeMapping (45, 88, ConsoleModifiers.Shift, 88),
-			new ScanCodeMapping (46, 67, 0, 99), // C
-			new ScanCodeMapping (46, 67, ConsoleModifiers.Shift, 67),
-			new ScanCodeMapping (47, 86, 0, 118), // V
-			new ScanCodeMapping (47, 86, ConsoleModifiers.Shift, 86),
-			new ScanCodeMapping (48, 66, 0, 98), // B
-			new ScanCodeMapping (48, 66, ConsoleModifiers.Shift, 66),
-			new ScanCodeMapping (49, 78, 0, 110), // N
-			new ScanCodeMapping (49, 78, ConsoleModifiers.Shift, 78),
-			new ScanCodeMapping (50, 77, 0, 109), // M
-			new ScanCodeMapping (50, 77, ConsoleModifiers.Shift, 77),
-			new ScanCodeMapping (51, 188, 0, 44), // OemComma
-			new ScanCodeMapping (51, 188, ConsoleModifiers.Shift, 59),
-			new ScanCodeMapping (52, 190, 0, 46), // OemPeriod
-			new ScanCodeMapping (52, 190, ConsoleModifiers.Shift, 58),
-			new ScanCodeMapping (53, 189, 0, 45), // OemMinus
-			new ScanCodeMapping (53, 189, ConsoleModifiers.Shift, 95),
-			new ScanCodeMapping (54, 16, 0, 0), // RShift
-			new ScanCodeMapping (54, 16, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (55, 44, 0, 0), // PrintScreen
-			new ScanCodeMapping (55, 44, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (56, 18, 0, 0), // Alt
-			new ScanCodeMapping (56, 18, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (57, 32, 0, 32), // Spacebar
-			new ScanCodeMapping (57, 32, ConsoleModifiers.Shift, 32),
-			new ScanCodeMapping (58, 20, 0, 0), // Caps
-			new ScanCodeMapping (58, 20, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (59, 112, 0, 0), // F1
-			new ScanCodeMapping (59, 112, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (60, 113, 0, 0), // F2
-			new ScanCodeMapping (60, 113, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (61, 114, 0, 0), // F3
-			new ScanCodeMapping (61, 114, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (62, 115, 0, 0), // F4
-			new ScanCodeMapping (62, 115, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (63, 116, 0, 0), // F5
-			new ScanCodeMapping (63, 116, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (64, 117, 0, 0), // F6
-			new ScanCodeMapping (64, 117, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (65, 118, 0, 0), // F7
-			new ScanCodeMapping (65, 118, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (66, 119, 0, 0), // F8
-			new ScanCodeMapping (66, 119, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (67, 120, 0, 0), // F9
-			new ScanCodeMapping (67, 120, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (68, 121, 0, 0), // F10
-			new ScanCodeMapping (68, 121, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (69, 144, 0, 0), // Num
-			new ScanCodeMapping (69, 144, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (70, 145, 0, 0), // Scroll
-			new ScanCodeMapping (70, 145, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (71, 36, 0, 0), // Home
-			new ScanCodeMapping (71, 36, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (72, 38, 0, 0), // UpArrow
-			new ScanCodeMapping (72, 38, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (73, 33, 0, 0), // PageUp
-			new ScanCodeMapping (73, 33, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (74, 109, 0, 45), // Subtract
-			new ScanCodeMapping (74, 109, ConsoleModifiers.Shift, 45),
-			new ScanCodeMapping (75, 37, 0, 0), // LeftArrow
-			new ScanCodeMapping (75, 37, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (76, 12, 0, 0), // Center
-			new ScanCodeMapping (76, 12, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (77, 39, 0, 0), // RightArrow
-			new ScanCodeMapping (77, 39, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (78, 107, 0, 43), // Add
-			new ScanCodeMapping (78, 107, ConsoleModifiers.Shift, 43),
-			new ScanCodeMapping (79, 35, 0, 0), // End
-			new ScanCodeMapping (79, 35, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (80, 40, 0, 0), // DownArrow
-			new ScanCodeMapping (80, 40, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (81, 34, 0, 0), // PageDown
-			new ScanCodeMapping (81, 34, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (82, 45, 0, 0), // Insert
-			new ScanCodeMapping (82, 45, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (83, 46, 0, 0), // Delete
-			new ScanCodeMapping (83, 46, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (86, 226, 0, 60), // OEM 102
-			new ScanCodeMapping (86, 226, ConsoleModifiers.Shift, 62),
-			new ScanCodeMapping (87, 122, 0, 0), // F11
-			new ScanCodeMapping (87, 122, ConsoleModifiers.Shift, 0),
-			new ScanCodeMapping (88, 123, 0, 0), // F12
-			new ScanCodeMapping (88, 123, ConsoleModifiers.Shift, 0)
-		};
-
-		/// <summary>
-		/// Decode a <see cref="ConsoleKeyInfo"/> that is using <see cref="ConsoleKey.Packet"/>.
-		/// </summary>
-		/// <param name="consoleKeyInfo">The console key info.</param>
-		/// <returns>The decoded <see cref="ConsoleKeyInfo"/> or the <paramref name="consoleKeyInfo"/>.</returns>
-		/// <remarks>If it's a <see cref="ConsoleKey.Packet"/> the <see cref="ConsoleKeyInfo.KeyChar"/> may be
-		/// a <see cref="ConsoleKeyInfo.Key"/> or a <see cref="ConsoleKeyInfo.KeyChar"/> value.
-		/// </remarks>
-		public static ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
-		{
-			if (consoleKeyInfo.Key != ConsoleKey.Packet) {
-				return consoleKeyInfo;
-			}
+		/// <summary>
+		/// CLEAR key.
+		/// </summary>
+		CLEAR = 0x0C,
 
 
-			return GetConsoleKeyFromKey (consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out _);
-		}
+		/// <summary>
+		/// ENTER key.
+		/// </summary>
+		RETURN = 0x0D,
+
+		/// <summary>
+		/// SHIFT key.
+		/// </summary>
+		SHIFT = 0x10,
+
+		/// <summary>
+		/// CTRL key.
+		/// </summary>
+		CONTROL = 0x11,
+
+		/// <summary>
+		/// ALT key.
+		/// </summary>
+		MENU = 0x12,
+
+		/// <summary>
+		/// PAUSE key.
+		/// </summary>
+		PAUSE = 0x13,
+
+		/// <summary>
+		/// CAPS LOCK key.
+		/// </summary>
+		CAPITAL = 0x14,
+
+		/// <summary>
+		/// IME Kana mode.
+		/// </summary>
+		KANA = 0x15,
+
+		/// <summary>
+		/// IME Hangul mode.
+		/// </summary>
+		HANGUL = 0x15,
+
+		/// <summary>
+		/// IME Junja mode.
+		/// </summary>
+		JUNJA = 0x17,
+
+		/// <summary>
+		/// IME final mode.
+		/// </summary>
+		FINAL = 0x18,
+
+		/// <summary>
+		/// IME Hanja mode.
+		/// </summary>
+		HANJA = 0x19,
+
+		/// <summary>
+		/// IME Kanji mode.
+		/// </summary>
+		KANJI = 0x19,
+
+		/// <summary>
+		/// ESC key.
+		/// </summary>
+		ESCAPE = 0x1B,
+
+		/// <summary>
+		/// IME convert.
+		/// </summary>
+		CONVERT = 0x1C,
+
+		/// <summary>
+		/// IME nonconvert.
+		/// </summary>
+		NONCONVERT = 0x1D,
+
+		/// <summary>
+		/// IME accept.
+		/// </summary>
+		ACCEPT = 0x1E,
+
+		/// <summary>
+		/// IME mode change request.
+		/// </summary>
+		MODECHANGE = 0x1F,
+
+		/// <summary>
+		/// SPACEBAR.
+		/// </summary>
+		SPACE = 0x20,
+
+		/// <summary>
+		/// PAGE UP key.
+		/// </summary>
+		PRIOR = 0x21,
+
+		/// <summary>
+		/// PAGE DOWN key.
+		/// </summary>
+		NEXT = 0x22,
+
+		/// <summary>
+		/// END key.
+		/// </summary>
+		END = 0x23,
+
+		/// <summary>
+		/// HOME key.
+		/// </summary>
+		HOME = 0x24,
+
+		/// <summary>
+		/// LEFT ARROW key.
+		/// </summary>
+		LEFT = 0x25,
+
+		/// <summary>
+		/// UP ARROW key.
+		/// </summary>
+		UP = 0x26,
+
+		/// <summary>
+		/// RIGHT ARROW key.
+		/// </summary>
+		RIGHT = 0x27,
+
+		/// <summary>
+		/// DOWN ARROW key.
+		/// </summary>
+		DOWN = 0x28,
+
+		/// <summary>
+		/// SELECT key.
+		/// </summary>
+		SELECT = 0x29,
+
+		/// <summary>
+		/// PRINT key.
+		/// </summary>
+		PRINT = 0x2A,
+
+		/// <summary>
+		/// EXECUTE key
+		/// </summary>
+		EXECUTE = 0x2B,
+
+		/// <summary>
+		/// PRINT SCREEN key
+		/// </summary>
+		SNAPSHOT = 0x2C,
+
+		/// <summary>
+		/// INS key
+		/// </summary>
+		INSERT = 0x2D,
+
+		/// <summary>
+		/// DEL key
+		/// </summary>
+		DELETE = 0x2E,
+
+		/// <summary>
+		/// HELP key
+		/// </summary>
+		HELP = 0x2F,
+
+		/// <summary>
+		/// Left Windows key (Natural keyboard)
+		/// </summary>
+		LWIN = 0x5B,
+
+		/// <summary>
+		/// Right Windows key (Natural keyboard)
+		/// </summary>
+		RWIN = 0x5C,
+
+		/// <summary>
+		/// Applications key (Natural keyboard)
+		/// </summary>
+		APPS = 0x5D,
+
+		/// <summary>
+		/// Computer Sleep key
+		/// </summary>
+		SLEEP = 0x5F,
+
+		/// <summary>
+		/// Numeric keypad 0 key
+		/// </summary>
+		NUMPAD0 = 0x60,
+
+		/// <summary>
+		/// Numeric keypad 1 key
+		/// </summary>
+		NUMPAD1 = 0x61,
+
+		/// <summary>
+		/// Numeric keypad 2 key
+		/// </summary>
+		NUMPAD2 = 0x62,
+
+		/// <summary>
+		/// Numeric keypad 3 key
+		/// </summary>
+		NUMPAD3 = 0x63,
+
+		/// <summary>
+		/// Numeric keypad 4 key
+		/// </summary>
+		NUMPAD4 = 0x64,
+
+		/// <summary>
+		/// Numeric keypad 5 key
+		/// </summary>
+		NUMPAD5 = 0x65,
+
+		/// <summary>
+		/// Numeric keypad 6 key
+		/// </summary>
+		NUMPAD6 = 0x66,
+
+		/// <summary>
+		/// Numeric keypad 7 key
+		/// </summary>
+		NUMPAD7 = 0x67,
+
+		/// <summary>
+		/// Numeric keypad 8 key
+		/// </summary>
+		NUMPAD8 = 0x68,
+
+		/// <summary>
+		/// Numeric keypad 9 key
+		/// </summary>
+		NUMPAD9 = 0x69,
+
+		/// <summary>
+		/// Multiply key
+		/// </summary>
+		MULTIPLY = 0x6A,
+
+		/// <summary>
+		/// Add key
+		/// </summary>
+		ADD = 0x6B,
+
+		/// <summary>
+		/// Separator key
+		/// </summary>
+		SEPARATOR = 0x6C,
+
+		/// <summary>
+		/// Subtract key
+		/// </summary>
+		SUBTRACT = 0x6D,
+
+		/// <summary>
+		/// Decimal key
+		/// </summary>
+		DECIMAL = 0x6E,
+
+		/// <summary>
+		/// Divide key
+		/// </summary>
+		DIVIDE = 0x6F,
+
+		/// <summary>
+		/// F1 key
+		/// </summary>
+		F1 = 0x70,
+
+		/// <summary>
+		/// F2 key
+		/// </summary>
+		F2 = 0x71,
+
+		/// <summary>
+		/// F3 key
+		/// </summary>
+		F3 = 0x72,
+
+		/// <summary>
+		/// F4 key
+		/// </summary>
+		F4 = 0x73,
+
+		/// <summary>
+		/// F5 key
+		/// </summary>
+		F5 = 0x74,
+
+		/// <summary>
+		/// F6 key
+		/// </summary>
+		F6 = 0x75,
+
+		/// <summary>
+		/// F7 key
+		/// </summary>
+		F7 = 0x76,
+
+		/// <summary>
+		/// F8 key
+		/// </summary>
+		F8 = 0x77,
+
+		/// <summary>
+		/// F9 key
+		/// </summary>
+		F9 = 0x78,
+
+		/// <summary>
+		/// F10 key
+		/// </summary>
+		F10 = 0x79,
+
+		/// <summary>
+		/// F11 key
+		/// </summary>
+		F11 = 0x7A,
+
+		/// <summary>
+		/// F12 key
+		/// </summary>
+		F12 = 0x7B,
+
+		/// <summary>
+		/// F13 key
+		/// </summary>
+		F13 = 0x7C,
+
+		/// <summary>
+		/// F14 key
+		/// </summary>
+		F14 = 0x7D,
+
+		/// <summary>
+		/// F15 key
+		/// </summary>
+		F15 = 0x7E,
+
+		/// <summary>
+		/// F16 key
+		/// </summary>
+		F16 = 0x7F,
+
+		/// <summary>
+		/// F17 key
+		/// </summary>
+		F17 = 0x80,
+
+		/// <summary>
+		/// F18 key
+		/// </summary>
+		F18 = 0x81,
+
+		/// <summary>
+		/// F19 key
+		/// </summary>
+		F19 = 0x82,
+
+		/// <summary>
+		/// F20 key
+		/// </summary>
+		F20 = 0x83,
+
+		/// <summary>
+		/// F21 key
+		/// </summary>
+		F21 = 0x84,
+
+		/// <summary>
+		/// F22 key
+		/// </summary>
+		F22 = 0x85,
+
+		/// <summary>
+		/// F23 key
+		/// </summary>
+		F23 = 0x86,
+
+		/// <summary>
+		/// F24 key
+		/// </summary>
+		F24 = 0x87,
+
+		/// <summary>
+		/// NUM LOCK key
+		/// </summary>
+		NUMLOCK = 0x90,
+
+		/// <summary>
+		/// SCROLL LOCK key
+		/// </summary>
+		SCROLL = 0x91,
+
+		/// <summary>
+		/// NEC PC-9800 kbd definition: '=' key on numpad
+		/// </summary>
+		OEM_NEC_EQUAL = 0x92,
+
+		/// <summary>
+		/// Fujitsu/OASYS kbd definition: 'Dictionary' key
+		/// </summary>
+		OEM_FJ_JISHO = 0x92,
+
+		/// <summary>
+		/// Fujitsu/OASYS kbd definition: 'Unregister word' key
+		/// </summary>
+		OEM_FJ_MASSHOU = 0x93,
+
+		/// <summary>
+		/// Fujitsu/OASYS kbd definition: 'Register word' key
+		/// </summary>
+		OEM_FJ_TOUROKU = 0x94,
+
+		/// <summary>
+		/// Fujitsu/OASYS kbd definition: 'Left OYAYUBI' key
+		/// </summary>
+		OEM_FJ_LOYA = 0x95,
+
+		/// <summary>
+		/// Fujitsu/OASYS kbd definition: 'Right OYAYUBI' key
+		/// </summary>
+		OEM_FJ_ROYA = 0x96,
+
+		/// <summary>
+		/// Left SHIFT key
+		/// </summary>
+		LSHIFT = 0xA0,
+
+		/// <summary>
+		/// Right SHIFT key
+		/// </summary>
+		RSHIFT = 0xA1,
+
+		/// <summary>
+		/// Left CONTROL key
+		/// </summary>
+		LCONTROL = 0xA2,
+
+		/// <summary>
+		/// Right CONTROL key
+		/// </summary>
+		RCONTROL = 0xA3,
+
+		/// <summary>
+		/// Left MENU key (Left Alt key)
+		/// </summary>
+		LMENU = 0xA4,
+
+		/// <summary>
+		/// Right MENU key (Right Alt key)
+		/// </summary>
+		RMENU = 0xA5,
+
+		/// <summary>
+		/// Browser Back key
+		/// </summary>
+		BROWSER_BACK = 0xA6,
+
+		/// <summary>
+		/// Browser Forward key
+		/// </summary>
+		BROWSER_FORWARD = 0xA7,
+
+		/// <summary>
+		/// Browser Refresh key
+		/// </summary>
+		BROWSER_REFRESH = 0xA8,
+
+		/// <summary>
+		/// Browser Stop key
+		/// </summary>
+		BROWSER_STOP = 0xA9,
+
+		/// <summary>
+		/// Browser Search key
+		/// </summary>
+		BROWSER_SEARCH = 0xAA,
+
+		/// <summary>
+		/// Browser Favorites key
+		/// </summary>
+		BROWSER_FAVORITES = 0xAB,
+
+		/// <summary>
+		/// Browser Home key
+		/// </summary>
+		BROWSER_HOME = 0xAC,
+
+		/// <summary>
+		/// Volume Mute key
+		/// </summary>
+		VOLUME_MUTE = 0xAD,
+
+		/// <summary>
+		/// Volume Down key
+		/// </summary>
+		VOLUME_DOWN = 0xAE,
+
+		/// <summary>
+		/// Volume Up key
+		/// </summary>
+		VOLUME_UP = 0xAF,
+
+		/// <summary>
+		/// Next Track key
+		/// </summary>
+		MEDIA_NEXT_TRACK = 0xB0,
+
+		/// <summary>
+		/// Previous Track key
+		/// </summary>
+		MEDIA_PREV_TRACK = 0xB1,
+
+		/// <summary>
+		/// Stop Media key
+		/// </summary>
+		MEDIA_STOP = 0xB2,
+
+		/// <summary>
+		/// Play/Pause Media key
+		/// </summary>
+		MEDIA_PLAY_PAUSE = 0xB3,
+
+		/// <summary>
+		/// Start Mail key
+		/// </summary>
+		LAUNCH_MAIL = 0xB4,
+
+		/// <summary>
+		/// Select Media key
+		/// </summary>
+		LAUNCH_MEDIA_SELECT = 0xB5,
+
+		/// <summary>
+		/// Start Application 1 key
+		/// </summary>
+		LAUNCH_APP1 = 0xB6,
+
+		/// <summary>
+		/// Start Application 2 key
+		/// </summary>
+		LAUNCH_APP2 = 0xB7,
+
+		/// <summary>
+		/// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ';:' key
+		/// </summary>
+		OEM_1 = 0xBA,
+
+		/// <summary>
+		/// For any country/region, the '+' key
+		/// </summary>
+		OEM_PLUS = 0xBB,
+
+		/// <summary>
+		/// For any country/region, the ',' key
+		/// </summary>
+		OEM_COMMA = 0xBC,
+
+		/// <summary>
+		/// For any country/region, the '-' key
+		/// </summary>
+		OEM_MINUS = 0xBD,
+
+		/// <summary>
+		/// For any country/region, the '.' key
+		/// </summary>
+		OEM_PERIOD = 0xBE,
+
+		/// <summary>
+		/// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '/?' key
+		/// </summary>
+		OEM_2 = 0xBF,
+
+		/// <summary>
+		/// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '`~' key
+		/// </summary>
+		OEM_3 = 0xC0,
+
+		/// <summary>
+		/// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '[{' key
+		/// </summary>
+		OEM_4 = 0xDB,
+
+		/// <summary>
+		/// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '\|' key
+		/// </summary>
+		OEM_5 = 0xDC,
+
+		/// <summary>
+		/// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ']}' key
+		/// </summary>
+		OEM_6 = 0xDD,
+
+		/// <summary>
+		/// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the 'single-quote/double-quote' key
+		/// </summary>
+		OEM_7 = 0xDE,
+
+		/// <summary>
+		/// Used for miscellaneous characters; it can vary by keyboard.
+		/// </summary>
+		OEM_8 = 0xDF,
+
+		/// <summary>
+		/// 'AX' key on Japanese AX kbd
+		/// </summary>
+		OEM_AX = 0xE1,
+
+		/// <summary>
+		/// Either the angle bracket key or the backslash key on the RT 102-key keyboard
+		/// </summary>
+		OEM_102 = 0xE2,
+
+		/// <summary>
+		/// Help key on ICO
+		/// </summary>
+		ICO_HELP = 0xE3,
+
+		/// <summary>
+		/// 00 key on ICO
+		/// </summary>
+		ICO_00 = 0xE4,
+
+		/// <summary>
+		/// Process key
+		/// </summary>
+		PROCESSKEY = 0xE5,
+
+		/// <summary>
+		/// Clear key on ICO
+		/// </summary>
+		ICO_CLEAR = 0xE6,
+
+		/// <summary>
+		/// Packet key to be used to pass Unicode characters as if they were keystrokes
+		/// </summary>
+		PACKET = 0xE7,
+
+		/// <summary>
+		/// Reset key
+		/// </summary>
+		OEM_RESET = 0xE9,
+
+		/// <summary>
+		/// Jump key
+		/// </summary>
+		OEM_JUMP = 0xEA,
+
+		/// <summary>
+		/// PA1 key
+		/// </summary>
+		OEM_PA1 = 0xEB,
+
+		/// <summary>
+		/// PA2 key
+		/// </summary>
+		OEM_PA2 = 0xEC,
+
+		/// <summary>
+		/// PA3 key
+		/// </summary>
+		OEM_PA3 = 0xED,
+
+		/// <summary>
+		/// WsCtrl key
+		/// </summary>
+		OEM_WSCTRL = 0xEE,
+
+		/// <summary>
+		/// CuSel key
+		/// </summary>
+		OEM_CUSEL = 0xEF,
+
+		/// <summary>
+		/// Attn key
+		/// </summary>
+		OEM_ATTN = 0xF0,
+
+		/// <summary>
+		/// Finish key
+		/// </summary>
+		OEM_FINISH = 0xF1,
+
+		/// <summary>
+		/// Copy key
+		/// </summary>
+		OEM_COPY = 0xF2,
+
+		/// <summary>
+		/// Auto key
+		/// </summary>
+		OEM_AUTO = 0xF3,
+
+		/// <summary>
+		/// Enlw key
+		/// </summary>
+		OEM_ENLW = 0xF4,
+
+		/// <summary>
+		/// BackTab key
+		/// </summary>
+		OEM_BACKTAB = 0xF5,
+
+		/// <summary>
+		/// Attn key
+		/// </summary>
+		ATTN = 0xF6,
+
+		/// <summary>
+		/// CrSel key
+		/// </summary>
+		CRSEL = 0xF7,
+
+		/// <summary>
+		/// ExSel key
+		/// </summary>
+		EXSEL = 0xF8,
+
+		/// <summary>
+		/// Erase EOF key
+		/// </summary>
+		EREOF = 0xF9,
+
+		/// <summary>
+		/// Play key
+		/// </summary>
+		PLAY = 0xFA,
+
+		/// <summary>
+		/// Zoom key
+		/// </summary>
+		ZOOM = 0xFB,
+
+		/// <summary>
+		/// Reserved
+		/// </summary>
+		NONAME = 0xFC,
+
+		/// <summary>
+		/// PA1 key
+		/// </summary>
+		PA1 = 0xFD,
+
+		/// <summary>
+		/// Clear key
+		/// </summary>
+		OEM_CLEAR = 0xFE
+	}
+
+	// BUGBUG: This database makes no sense. It is not possible to map a VK code to a character without knowing the keyboard layout
+	//         It should be deleted.
+	static HashSet<ScanCodeMapping> _scanCodes = new HashSet<ScanCodeMapping> {
+		new (1, VK.ESCAPE, 0, '\u001B'), // Escape
+		new (1, VK.ESCAPE, ConsoleModifiers.Shift, '\u001B'),
+		new (2, (VK)'1', 0, '1'), // D1
+		new (2, (VK)'1', ConsoleModifiers.Shift, '!'),
+		new (3, (VK)'2', 0, '2'), // D2
+		new (3, (VK)'2', ConsoleModifiers.Shift, '\"'), // BUGBUG: This is true for Portugese keyboard, but not ENG (@) or DEU (")
+		new (3, (VK)'2', ConsoleModifiers.Alt | ConsoleModifiers.Control, '@'),
+		new (4, (VK)'3', 0, '3'), // D3
+		new (4, (VK)'3', ConsoleModifiers.Shift, '#'),
+		new (4, (VK)'3', ConsoleModifiers.Alt | ConsoleModifiers.Control, '£'),
+		new (5, (VK)'4', 0, '4'), // D4
+		new (5, (VK)'4', ConsoleModifiers.Shift, '$'),
+		new (5, (VK)'4', ConsoleModifiers.Alt | ConsoleModifiers.Control, '§'),
+		new (6, (VK)'5', 0, '5'), // D5
+		new (6, (VK)'5', ConsoleModifiers.Shift, '%'),
+		new (6, (VK)'5', ConsoleModifiers.Alt | ConsoleModifiers.Control, '€'),
+		new (7, (VK)'6', 0, '6'), // D6
+		new (7, (VK)'6', ConsoleModifiers.Shift, '&'),
+		new (8, (VK)'7', 0, '7'), // D7
+		new (8, (VK)'7', ConsoleModifiers.Shift, '/'),
+		new (8, (VK)'7', ConsoleModifiers.Alt | ConsoleModifiers.Control, '{'),
+		new (9, (VK)'8', 0, '8'), // D8
+		new (9, (VK)'8', ConsoleModifiers.Shift, '('),
+		new (9, (VK)'8', ConsoleModifiers.Alt | ConsoleModifiers.Control, '['),
+		new (10, (VK)'9', 0, '9'), // D9
+		new (10, (VK)'9', ConsoleModifiers.Shift, ')'),
+		new (10, (VK)'9', ConsoleModifiers.Alt | ConsoleModifiers.Control, ']'),
+		new (11, (VK)'0', 0, '0'), // D0
+		new (11, (VK)'0', ConsoleModifiers.Shift, '='),
+		new (11, (VK)'0', ConsoleModifiers.Alt | ConsoleModifiers.Control, '}'),
+		new (12, VK.OEM_4, 0, '\''), // Oem4
+		new (12, VK.OEM_4, ConsoleModifiers.Shift, '?'),
+		new (13, VK.OEM_6, 0, '+'), // Oem6
+		new (13, VK.OEM_6, ConsoleModifiers.Shift, '*'),
+		new (14, VK.BACK, 0, '\u0008'), // Backspace
+		new (14, VK.BACK, ConsoleModifiers.Shift, '\u0008'),
+		new (15, VK.TAB, 0, '\u0009'), // Tab
+		new (15, VK.TAB, ConsoleModifiers.Shift, '\u000F'),
+		new (16, (VK)'Q', 0, 'q'), // Q
+		new (16, (VK)'Q', ConsoleModifiers.Shift, 'Q'),
+		new (17, (VK)'W', 0, 'w'), // W
+		new (17, (VK)'W', ConsoleModifiers.Shift, 'W'),
+		new (18, (VK)'E', 0, 'e'), // E
+		new (18, (VK)'E', ConsoleModifiers.Shift, 'E'),
+		new (19, (VK)'R', 0, 'r'), // R
+		new (19, (VK)'R', ConsoleModifiers.Shift, 'R'),
+		new (20, (VK)'T', 0, 't'), // T
+		new (20, (VK)'T', ConsoleModifiers.Shift, 'T'),
+		new (21, (VK)'Y', 0, 'y'), // Y
+		new (21, (VK)'Y', ConsoleModifiers.Shift, 'Y'),
+		new (22, (VK)'U', 0, 'u'), // U
+		new (22, (VK)'U', ConsoleModifiers.Shift, 'U'),
+		new (23, (VK)'I', 0, 'i'), // I
+		new (23, (VK)'I', ConsoleModifiers.Shift, 'I'),
+		new (24, (VK)'O', 0, 'o'), // O
+		new (24, (VK)'O', ConsoleModifiers.Shift, 'O'),
+		new (25, (VK)'P', 0, 'p'), // P
+		new (25, (VK)'P', ConsoleModifiers.Shift, 'P'),
+		new (26, VK.OEM_PLUS, 0, '+'), // OemPlus
+		new (26, VK.OEM_PLUS, ConsoleModifiers.Shift, '*'),
+		new (26, VK.OEM_PLUS, ConsoleModifiers.Alt | ConsoleModifiers.Control, '¨'),
+		new (27, VK.OEM_1, 0, '´'), // Oem1
+		new (27, VK.OEM_1, ConsoleModifiers.Shift, '`'),
+		new (28, VK.RETURN, 0, '\u000D'), // Enter
+		new (28, VK.RETURN, ConsoleModifiers.Shift, '\u000D'),
+		new (29, VK.CONTROL, 0, '\0'), // Control
+		new (29, VK.CONTROL, ConsoleModifiers.Shift, '\0'),
+		new (30, (VK)'A', 0, 'a'), // A
+		new (30, (VK)'A', ConsoleModifiers.Shift, 'A'),
+		new (31, (VK)'S', 0, 's'), // S
+		new (31, (VK)'S', ConsoleModifiers.Shift, 'S'),
+		new (32, (VK)'D', 0, 'd'), // D
+		new (32, (VK)'D', ConsoleModifiers.Shift, 'D'),
+		new (33, (VK)'F', 0, 'f'), // F
+		new (33, (VK)'F', ConsoleModifiers.Shift, 'F'),
+		new (34, (VK)'G', 0, 'g'), // G
+		new (34, (VK)'G', ConsoleModifiers.Shift, 'G'),
+		new (35, (VK)'H', 0, 'h'), // H
+		new (35, (VK)'H', ConsoleModifiers.Shift, 'H'),
+		new (36, (VK)'J', 0, 'j'), // J
+		new (36, (VK)'J', ConsoleModifiers.Shift, 'J'),
+		new (37, (VK)'K', 0, 'k'), // K
+		new (37, (VK)'K', ConsoleModifiers.Shift, 'K'),
+		new (38, (VK)'L', 0, 'l'), // L
+		new (38, (VK)'L', ConsoleModifiers.Shift, 'L'),
+		new (39, VK.OEM_3, 0, '`'), // Oem3 (Backtick/Grave)
+		new (39, VK.OEM_3, ConsoleModifiers.Shift, '~'),
+		new (40, VK.OEM_7, 0, '\''), // Oem7 (Single Quote)
+		new (40, VK.OEM_7, ConsoleModifiers.Shift, '\"'),
+		new (41, VK.OEM_5, 0, '\\'), // Oem5 (Backslash)
+		new (41, VK.OEM_5, ConsoleModifiers.Shift, '|'),
+		new (42, VK.LSHIFT, 0, '\0'), // Left Shift
+		new (42, VK.LSHIFT, ConsoleModifiers.Shift, '\0'),
+		new (43, VK.OEM_2, 0, '/'), // Oem2 (Forward Slash)
+		new (43, VK.OEM_2, ConsoleModifiers.Shift, '?'),
+		new (44, (VK)'Z', 0, 'z'), // Z
+		new (44, (VK)'Z', ConsoleModifiers.Shift, 'Z'),
+		new (45, (VK)'X', 0, 'x'), // X
+		new (45, (VK)'X', ConsoleModifiers.Shift, 'X'),
+		new (46, (VK)'C', 0, 'c'), // C
+		new (46, (VK)'C', ConsoleModifiers.Shift, 'C'),
+		new (47, (VK)'V', 0, 'v'), // V
+		new (47, (VK)'V', ConsoleModifiers.Shift, 'V'),
+		new (48, (VK)'B', 0, 'b'), // B
+		new (48, (VK)'B', ConsoleModifiers.Shift, 'B'),
+		new (49, (VK)'N', 0, 'n'), // N
+		new (49, (VK)'N', ConsoleModifiers.Shift, 'N'),
+		new (50, (VK)'M', 0, 'm'), // M
+		new (50, (VK)'M', ConsoleModifiers.Shift, 'M'),
+		new (51, VK.OEM_COMMA, 0, ','), // OemComma
+		new (51, VK.OEM_COMMA, ConsoleModifiers.Shift, '<'),
+		new (52, VK.OEM_PERIOD, 0, '.'), // OemPeriod
+		new (52, VK.OEM_PERIOD, ConsoleModifiers.Shift, '>'),
+		new (53, VK.OEM_MINUS, 0, '-'), // OemMinus
+		new (53, VK.OEM_MINUS, ConsoleModifiers.Shift, '_'),
+		new (54, VK.RSHIFT, 0, '\0'), // Right Shift
+		new (54, VK.RSHIFT, ConsoleModifiers.Shift, '\0'),
+		new (55, VK.PRINT, 0, '\0'), // Print Screen
+		new (55, VK.PRINT, ConsoleModifiers.Shift, '\0'),
+		new (56, VK.LMENU, 0, '\0'), // Alt
+		new (56, VK.LMENU, ConsoleModifiers.Shift, '\0'),
+		new (57, VK.SPACE, 0, ' '), // Spacebar
+		new (57, VK.SPACE, ConsoleModifiers.Shift, ' '),
+		new (58, VK.CAPITAL, 0, '\0'), // Caps Lock
+		new (58, VK.CAPITAL, ConsoleModifiers.Shift, '\0'),
+		new (59, VK.F1, 0, '\0'), // F1
+		new (59, VK.F1, ConsoleModifiers.Shift, '\0'),
+		new (60, VK.F2, 0, '\0'), // F2
+		new (60, VK.F2, ConsoleModifiers.Shift, '\0'),
+		new (61, VK.F3, 0, '\0'), // F3
+		new (61, VK.F3, ConsoleModifiers.Shift, '\0'),
+		new (62, VK.F4, 0, '\0'), // F4
+		new (62, VK.F4, ConsoleModifiers.Shift, '\0'),
+		new (63, VK.F5, 0, '\0'), // F5
+		new (63, VK.F5, ConsoleModifiers.Shift, '\0'),
+		new (64, VK.F6, 0, '\0'), // F6
+		new (64, VK.F6, ConsoleModifiers.Shift, '\0'),
+		new (65, VK.F7, 0, '\0'), // F7
+		new (65, VK.F7, ConsoleModifiers.Shift, '\0'),
+		new (66, VK.F8, 0, '\0'), // F8
+		new (66, VK.F8, ConsoleModifiers.Shift, '\0'),
+		new (67, VK.F9, 0, '\0'), // F9
+		new (67, VK.F9, ConsoleModifiers.Shift, '\0'),
+		new (68, VK.F10, 0, '\0'), // F10
+		new (68, VK.F10, ConsoleModifiers.Shift, '\0'),
+		new (69, VK.NUMLOCK, 0, '\0'), // Num Lock
+		new (69, VK.NUMLOCK, ConsoleModifiers.Shift, '\0'),
+		new (70, VK.SCROLL, 0, '\0'), // Scroll Lock
+		new (70, VK.SCROLL, ConsoleModifiers.Shift, '\0'),
+		new (71, VK.HOME, 0, '\0'), // Home
+		new (71, VK.HOME, ConsoleModifiers.Shift, '\0'),
+		new (72, VK.UP, 0, '\0'), // Up Arrow
+		new (72, VK.UP, ConsoleModifiers.Shift, '\0'),
+		new (73, VK.PRIOR, 0, '\0'), // Page Up
+		new (73, VK.PRIOR, ConsoleModifiers.Shift, '\0'),
+		new (74, VK.SUBTRACT, 0, '-'), // Subtract (Num Pad '-')
+		new (74, VK.SUBTRACT, ConsoleModifiers.Shift, '-'),
+		new (75, VK.LEFT, 0, '\0'), // Left Arrow
+		new (75, VK.LEFT, ConsoleModifiers.Shift, '\0'),
+		new (76, VK.CLEAR, 0, '\0'), // Center key (Num Pad 5 with Num Lock off)
+		new (76, VK.CLEAR, ConsoleModifiers.Shift, '\0'),
+		new (77, VK.RIGHT, 0, '\0'), // Right Arrow
+		new (77, VK.RIGHT, ConsoleModifiers.Shift, '\0'),
+		new (78, VK.ADD, 0, '+'), // Add (Num Pad '+')
+		new (78, VK.ADD, ConsoleModifiers.Shift, '+'),
+		new (79, VK.END, 0, '\0'), // End
+		new (79, VK.END, ConsoleModifiers.Shift, '\0'),
+		new (80, VK.DOWN, 0, '\0'), // Down Arrow
+		new (80, VK.DOWN, ConsoleModifiers.Shift, '\0'),
+		new (81, VK.NEXT, 0, '\0'), // Page Down
+		new (81, VK.NEXT, ConsoleModifiers.Shift, '\0'),
+		new (82, VK.INSERT, 0, '\0'), // Insert
+		new (82, VK.INSERT, ConsoleModifiers.Shift, '\0'),
+		new (83, VK.DELETE, 0, '\0'), // Delete
+		new (83, VK.DELETE, ConsoleModifiers.Shift, '\0'),
+		new (86, VK.OEM_102, 0, '<'), // OEM 102 (Typically '<' or '|' key next to Left Shift)
+		new (86, VK.OEM_102, ConsoleModifiers.Shift, '>'),
+		new (87, VK.F11, 0, '\0'), // F11
+		new (87, VK.F11, ConsoleModifiers.Shift, '\0'),
+		new (88, VK.F12, 0, '\0'), // F12
+		new (88, VK.F12, ConsoleModifiers.Shift, '\0')
+	};
+
+	/// <summary>
+	/// Decode a <see cref="ConsoleKeyInfo"/> that is using <see cref="ConsoleKey.Packet"/>.
+	/// </summary>
+	/// <param name="consoleKeyInfo">The console key info.</param>
+	/// <returns>The decoded <see cref="ConsoleKeyInfo"/> or the <paramref name="consoleKeyInfo"/>.</returns>
+	/// <remarks>If it's a <see cref="ConsoleKey.Packet"/> the <see cref="ConsoleKeyInfo.KeyChar"/> may be
+	/// a <see cref="ConsoleKeyInfo.Key"/> or a <see cref="ConsoleKeyInfo.KeyChar"/> value.
+	/// </remarks>
+	public static ConsoleKeyInfo DecodeVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
+	{
+		if (consoleKeyInfo.Key != ConsoleKey.Packet) {
+			return consoleKeyInfo;
+		}
+
+		return GetConsoleKeyInfoFromKeyChar (consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out _);
+	}
+
+	/// <summary>
+	/// Encode the <see cref="ConsoleKeyInfo.KeyChar"/> with the <see cref="ConsoleKeyInfo.Key"/>
+	/// if the first a byte length, otherwise only the KeyChar is considered and searched on the database.
+	/// </summary>
+	/// <param name="consoleKeyInfo">The console key info.</param>
+	/// <returns>The encoded KeyChar with the Key if both can be shifted, otherwise only the KeyChar.</returns>
+	/// <remarks>This is useful to use with the <see cref="ConsoleKey.Packet"/>.</remarks>
+	public static char EncodeKeyCharForVKPacket (ConsoleKeyInfo consoleKeyInfo)
+	{
+		char keyChar = consoleKeyInfo.KeyChar;
+		ConsoleKey consoleKey = consoleKeyInfo.Key;
+		if (keyChar != 0 && consoleKeyInfo.KeyChar < byte.MaxValue && consoleKey == ConsoleKey.None) {
+			// try to get the ConsoleKey
+			var scode = _scanCodes.FirstOrDefault ((e) => e.UnicodeChar == keyChar);
+			if (scode != null) {
+				consoleKey = (ConsoleKey)scode.VirtualKey;
+			}
+		}
+		if (keyChar < byte.MaxValue && consoleKey != ConsoleKey.None) {
+			keyChar = (char)(consoleKeyInfo.KeyChar << 8 | (byte)consoleKey);
+		}
+
+		return keyChar;
 	}
 	}
-}
+}

+ 71 - 87
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -14,12 +14,12 @@ namespace Terminal.Gui;
 /// <summary>
 /// <summary>
 /// This is the Curses driver for the gui.cs/Terminal framework.
 /// This is the Curses driver for the gui.cs/Terminal framework.
 /// </summary>
 /// </summary>
-internal class CursesDriver : ConsoleDriver {
-
+class CursesDriver : ConsoleDriver {
 	public override int Cols {
 	public override int Cols {
 		get => Curses.Cols;
 		get => Curses.Cols;
 		internal set => Curses.Cols = value;
 		internal set => Curses.Cols = value;
 	}
 	}
+
 	public override int Rows {
 	public override int Rows {
 		get => Curses.Lines;
 		get => Curses.Lines;
 		internal set => Curses.Lines = value;
 		internal set => Curses.Lines = value;
@@ -30,6 +30,7 @@ internal class CursesDriver : ConsoleDriver {
 
 
 	public override string GetVersionInfo () => $"{Curses.curses_version ()}";
 	public override string GetVersionInfo () => $"{Curses.curses_version ()}";
 	UnixMainLoop _mainLoopDriver = null;
 	UnixMainLoop _mainLoopDriver = null;
+
 	public override bool SupportsTrueColor => false;
 	public override bool SupportsTrueColor => false;
 
 
 	object _processInputToken;
 	object _processInputToken;
@@ -132,11 +133,9 @@ internal class CursesDriver : ConsoleDriver {
 		}
 		}
 	}
 	}
 
 
-	public override bool IsRuneSupported (Rune rune)
-	{
+	public override bool IsRuneSupported (Rune rune) =>
 		// See Issue #2615 - CursesDriver is broken with non-BMP characters
 		// See Issue #2615 - CursesDriver is broken with non-BMP characters
-		return base.IsRuneSupported (rune) && rune.IsBmp;
-	}
+		base.IsRuneSupported (rune) && rune.IsBmp;
 
 
 	public override void Refresh ()
 	public override void Refresh ()
 	{
 	{
@@ -153,7 +152,6 @@ internal class CursesDriver : ConsoleDriver {
 	}
 	}
 
 
 	#region Color Handling
 	#region Color Handling
-
 	/// <summary>
 	/// <summary>
 	/// Creates an Attribute from the provided curses-based foreground and background color numbers
 	/// Creates an Attribute from the provided curses-based foreground and background color numbers
 	/// </summary>
 	/// </summary>
@@ -162,16 +160,17 @@ internal class CursesDriver : ConsoleDriver {
 	/// <returns></returns>
 	/// <returns></returns>
 	static Attribute MakeColor (short foreground, short background)
 	static Attribute MakeColor (short foreground, short background)
 	{
 	{
-		var v = (short)((int)foreground | background << 4);
+		short v = (short)((int)foreground | background << 4);
 
 
 		// TODO: for TrueColor - Use InitExtendedPair
 		// TODO: for TrueColor - Use InitExtendedPair
 		Curses.InitColorPair (v, foreground, background);
 		Curses.InitColorPair (v, foreground, background);
 		return new Attribute (
 		return new Attribute (
-			platformColor: Curses.ColorPair (v),
-			foreground: CursesColorNumberToColorName (foreground),
-			background: CursesColorNumberToColorName (background));
+			Curses.ColorPair (v),
+			CursesColorNumberToColorName (foreground),
+			CursesColorNumberToColorName (background));
 	}
 	}
 
 
+	/// <inheritdoc/>
 	/// <remarks>
 	/// <remarks>
 	/// In the CursesDriver, colors are encoded as an int. 
 	/// In the CursesDriver, colors are encoded as an int. 
 	/// The foreground color is stored in the most significant 4 bits, 
 	/// The foreground color is stored in the most significant 4 bits, 
@@ -184,9 +183,9 @@ internal class CursesDriver : ConsoleDriver {
 			return MakeColor (ColorNameToCursesColorNumber (foreground.ColorName), ColorNameToCursesColorNumber (background.ColorName));
 			return MakeColor (ColorNameToCursesColorNumber (foreground.ColorName), ColorNameToCursesColorNumber (background.ColorName));
 		} else {
 		} else {
 			return new Attribute (
 			return new Attribute (
-				platformColor: 0,
-				foreground: foreground,
-				background: background);
+				0,
+				foreground,
+				background);
 		}
 		}
 	}
 	}
 
 
@@ -267,7 +266,6 @@ internal class CursesDriver : ConsoleDriver {
 		}
 		}
 		throw new ArgumentException ("Invalid curses color code");
 		throw new ArgumentException ("Invalid curses color code");
 	}
 	}
-
 	#endregion
 	#endregion
 
 
 	public override void UpdateCursor ()
 	public override void UpdateCursor ()
@@ -367,8 +365,8 @@ internal class CursesDriver : ConsoleDriver {
 		case Curses.KeyEnd: return KeyCode.End;
 		case Curses.KeyEnd: return KeyCode.End;
 		case Curses.KeyNPage: return KeyCode.PageDown;
 		case Curses.KeyNPage: return KeyCode.PageDown;
 		case Curses.KeyPPage: return KeyCode.PageUp;
 		case Curses.KeyPPage: return KeyCode.PageUp;
-		case Curses.KeyDeleteChar: return KeyCode.DeleteChar;
-		case Curses.KeyInsertChar: return KeyCode.InsertChar;
+		case Curses.KeyDeleteChar: return KeyCode.Delete;
+		case Curses.KeyInsertChar: return KeyCode.Insert;
 		case Curses.KeyTab: return KeyCode.Tab;
 		case Curses.KeyTab: return KeyCode.Tab;
 		case Curses.KeyBackTab: return KeyCode.Tab | KeyCode.ShiftMask;
 		case Curses.KeyBackTab: return KeyCode.Tab | KeyCode.ShiftMask;
 		case Curses.KeyBackspace: return KeyCode.Backspace;
 		case Curses.KeyBackspace: return KeyCode.Backspace;
@@ -423,12 +421,12 @@ internal class CursesDriver : ConsoleDriver {
 	internal void ProcessInput ()
 	internal void ProcessInput ()
 	{
 	{
 		int wch;
 		int wch;
-		var code = Curses.get_wch (out wch);
+		int code = Curses.get_wch (out wch);
 		//System.Diagnostics.Debug.WriteLine ($"code: {code}; wch: {wch}");
 		//System.Diagnostics.Debug.WriteLine ($"code: {code}; wch: {wch}");
 		if (code == Curses.ERR) {
 		if (code == Curses.ERR) {
 			return;
 			return;
 		}
 		}
-		KeyCode k = KeyCode.Null;
+		var k = KeyCode.Null;
 
 
 		if (code == Curses.KEY_CODE_YES) {
 		if (code == Curses.KEY_CODE_YES) {
 			while (code == Curses.KEY_CODE_YES && wch == Curses.KeyResize) {
 			while (code == Curses.KEY_CODE_YES && wch == Curses.KeyResize) {
@@ -443,11 +441,11 @@ internal class CursesDriver : ConsoleDriver {
 
 
 				while (wch2 == Curses.KeyMouse) {
 				while (wch2 == Curses.KeyMouse) {
 					Key kea = null;
 					Key kea = null;
-					ConsoleKeyInfo [] cki = new ConsoleKeyInfo [] {
-							new ConsoleKeyInfo ((char)KeyCode.Esc, 0, false, false, false),
-							new ConsoleKeyInfo ('[', 0, false, false, false),
-							new ConsoleKeyInfo ('<', 0, false, false, false)
-						};
+					var cki = new ConsoleKeyInfo [] {
+						new ((char)KeyCode.Esc, 0, false, false, false),
+						new ('[', 0, false, false, false),
+						new ('<', 0, false, false, false)
+					};
 					code = 0;
 					code = 0;
 					HandleEscSeqResponse (ref code, ref k, ref wch2, ref kea, ref cki);
 					HandleEscSeqResponse (ref code, ref k, ref wch2, ref kea, ref cki);
 				}
 				}
@@ -503,27 +501,27 @@ internal class CursesDriver : ConsoleDriver {
 				} else if (wch2 >= (uint)KeyCode.D0 && wch2 <= (uint)KeyCode.D9) {
 				} else if (wch2 >= (uint)KeyCode.D0 && wch2 <= (uint)KeyCode.D9) {
 					k = (KeyCode)((uint)KeyCode.AltMask + (uint)KeyCode.D0 + (wch2 - (uint)KeyCode.D0));
 					k = (KeyCode)((uint)KeyCode.AltMask + (uint)KeyCode.D0 + (wch2 - (uint)KeyCode.D0));
 				} else if (wch2 == Curses.KeyCSI) {
 				} else if (wch2 == Curses.KeyCSI) {
-					ConsoleKeyInfo [] cki = new ConsoleKeyInfo [] {
-							new ConsoleKeyInfo ((char)KeyCode.Esc, 0, false, false, false),
-							new ConsoleKeyInfo ('[', 0, false, false, false)
-						};
+					var cki = new ConsoleKeyInfo [] {
+						new ((char)KeyCode.Esc, 0, false, false, false),
+						new ('[', 0, false, false, false)
+					};
 					HandleEscSeqResponse (ref code, ref k, ref wch2, ref key, ref cki);
 					HandleEscSeqResponse (ref code, ref k, ref wch2, ref key, ref cki);
 					return;
 					return;
 				} else {
 				} else {
 					// Unfortunately there are no way to differentiate Ctrl+Alt+alfa and Ctrl+Shift+Alt+alfa.
 					// Unfortunately there are no way to differentiate Ctrl+Alt+alfa and Ctrl+Shift+Alt+alfa.
 					if (((KeyCode)wch2 & KeyCode.CtrlMask) != 0) {
 					if (((KeyCode)wch2 & KeyCode.CtrlMask) != 0) {
-						k = (KeyCode)((uint)KeyCode.CtrlMask + (wch2 & ~((int)KeyCode.CtrlMask)));
+						k = (KeyCode)((uint)KeyCode.CtrlMask + (wch2 & ~(int)KeyCode.CtrlMask));
 					}
 					}
 					if (wch2 == 0) {
 					if (wch2 == 0) {
 						k = KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.Space;
 						k = KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.Space;
 					} else if (wch >= (uint)KeyCode.A && wch <= (uint)KeyCode.Z) {
 					} else if (wch >= (uint)KeyCode.A && wch <= (uint)KeyCode.Z) {
 						k = KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.Space;
 						k = KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.Space;
 					} else if (wch2 < 256) {
 					} else if (wch2 < 256) {
-						k = (KeyCode)wch2 | KeyCode.AltMask;
+						k = (KeyCode)wch2;// | KeyCode.AltMask;
 					} else {
 					} else {
 						k = (KeyCode)((uint)(KeyCode.AltMask | KeyCode.CtrlMask) + wch2);
 						k = (KeyCode)((uint)(KeyCode.AltMask | KeyCode.CtrlMask) + wch2);
 					}
 					}
-				}
+				} 
 				key = new Key (k);
 				key = new Key (k);
 			} else {
 			} else {
 				key = new Key (KeyCode.Esc);
 				key = new Key (KeyCode.Esc);
@@ -545,9 +543,11 @@ internal class CursesDriver : ConsoleDriver {
 				}
 				}
 			} else if (wch >= (uint)KeyCode.A && wch <= (uint)KeyCode.Z) {
 			} else if (wch >= (uint)KeyCode.A && wch <= (uint)KeyCode.Z) {
 				k = (KeyCode)wch | KeyCode.ShiftMask;
 				k = (KeyCode)wch | KeyCode.ShiftMask;
-			} else if (wch <= 'z') {
-				k = (KeyCode)wch & ~KeyCode.Space;
 			} 
 			} 
+			
+			if (wch == '\n' || wch == '\r') {
+				k = KeyCode.Enter;
+			}
 			OnKeyDown (new Key (k));
 			OnKeyDown (new Key (k));
 			OnKeyUp (new Key (k));
 			OnKeyUp (new Key (k));
 		}
 		}
@@ -561,7 +561,7 @@ internal class CursesDriver : ConsoleDriver {
 			code = Curses.get_wch (out wch2);
 			code = Curses.get_wch (out wch2);
 			var consoleKeyInfo = new ConsoleKeyInfo ((char)wch2, 0, false, false, false);
 			var consoleKeyInfo = new ConsoleKeyInfo ((char)wch2, 0, false, false, false);
 			if (wch2 == 0 || wch2 == 27 || wch2 == Curses.KeyMouse) {
 			if (wch2 == 0 || wch2 == 27 || wch2 == Curses.KeyMouse) {
-				EscSeqUtils.DecodeEscSeq (null, ref consoleKeyInfo, ref ck, cki, ref mod, out _, out _, out _, out _, out bool isKeyMouse, out List<MouseFlags> mouseFlags, out Point pos, out _, ProcessMouseEvent);
+				EscSeqUtils.DecodeEscSeq (null, ref consoleKeyInfo, ref ck, cki, ref mod, out _, out _, out _, out _, out bool isKeyMouse, out var mouseFlags, out var pos, out _, ProcessMouseEvent);
 				if (isKeyMouse) {
 				if (isKeyMouse) {
 					foreach (var mf in mouseFlags) {
 					foreach (var mf in mouseFlags) {
 						ProcessMouseEvent (mf, pos);
 						ProcessMouseEvent (mf, pos);
@@ -572,9 +572,8 @@ internal class CursesDriver : ConsoleDriver {
 							false, false, false), cki);
 							false, false, false), cki);
 					}
 					}
 				} else {
 				} else {
-					k = ConsoleKeyMapping.MapConsoleKeyToKey (consoleKeyInfo.Key, out _);
-					k = ConsoleKeyMapping.MapKeyModifiers (consoleKeyInfo, k);
-					keyEventArgs = new (k);
+					k = ConsoleKeyMapping.MapConsoleKeyInfoToKeyCode (consoleKeyInfo);
+					keyEventArgs = new Key (k);
 					OnKeyDown (keyEventArgs);
 					OnKeyDown (keyEventArgs);
 				}
 				}
 			} else {
 			} else {
@@ -587,36 +586,27 @@ internal class CursesDriver : ConsoleDriver {
 
 
 	void ProcessMouseEvent (MouseFlags mouseFlag, Point pos)
 	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)) {
+		bool WasButtonReleased (MouseFlags flag) => flag.HasFlag (MouseFlags.Button1Released) ||
+							flag.HasFlag (MouseFlags.Button2Released) ||
+							flag.HasFlag (MouseFlags.Button3Released) ||
+							flag.HasFlag (MouseFlags.Button4Released);
+
+		bool IsButtonNotPressed (MouseFlags flag) => !flag.HasFlag (MouseFlags.Button1Pressed) &&
+								!flag.HasFlag (MouseFlags.Button2Pressed) &&
+								!flag.HasFlag (MouseFlags.Button3Pressed) &&
+								!flag.HasFlag (MouseFlags.Button4Pressed);
+
+		bool IsButtonClickedOrDoubleClicked (MouseFlags flag) => 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;
 			return;
 		}
 		}
 
 
@@ -638,7 +628,7 @@ internal class CursesDriver : ConsoleDriver {
 		//	// If xclip is installed on Linux under WSL, this will return true.
 		//	// If xclip is installed on Linux under WSL, this will return true.
 		//	return false;
 		//	return false;
 		//}
 		//}
-		var (exitCode, result) = ClipboardProcessRunner.Bash ("uname -a", waitForOutput: true);
+		(int exitCode, string result) = ClipboardProcessRunner.Bash ("uname -a", waitForOutput: true);
 		if (exitCode == 0 && result.Contains ("microsoft") && result.Contains ("WSL")) {
 		if (exitCode == 0 && result.Contains ("microsoft") && result.Contains ("WSL")) {
 			return true;
 			return true;
 		}
 		}
@@ -675,8 +665,9 @@ internal class CursesDriver : ConsoleDriver {
 	{
 	{
 		visibility = CursorVisibility.Invisible;
 		visibility = CursorVisibility.Invisible;
 
 
-		if (!_currentCursorVisibility.HasValue)
+		if (!_currentCursorVisibility.HasValue) {
 			return false;
 			return false;
+		}
 
 
 		visibility = _currentCursorVisibility.Value;
 		visibility = _currentCursorVisibility.Value;
 
 
@@ -691,11 +682,11 @@ internal class CursesDriver : ConsoleDriver {
 		}
 		}
 
 
 		if (!RunningUnitTests) {
 		if (!RunningUnitTests) {
-			Curses.curs_set (((int)visibility >> 16) & 0x000000FF);
+			Curses.curs_set ((int)visibility >> 16 & 0x000000FF);
 		}
 		}
 
 
 		if (visibility != CursorVisibility.Invisible) {
 		if (visibility != CursorVisibility.Invisible) {
-			Console.Out.Write (EscSeqUtils.CSI_SetCursorStyle ((EscSeqUtils.DECSCUSR_Style)(((int)visibility >> 24) & 0xFF)));
+			Console.Out.Write (EscSeqUtils.CSI_SetCursorStyle ((EscSeqUtils.DECSCUSR_Style)((int)visibility >> 24 & 0xFF)));
 		}
 		}
 
 
 		_currentCursorVisibility = visibility;
 		_currentCursorVisibility = visibility;
@@ -704,17 +695,14 @@ internal class CursesDriver : ConsoleDriver {
 	}
 	}
 
 
 	/// <inheritdoc/>
 	/// <inheritdoc/>
-	public override bool EnsureCursorVisibility ()
-	{
-		return false;
-	}
+	public override bool EnsureCursorVisibility () => false;
 
 
 	public override void SendKeys (char keyChar, ConsoleKey consoleKey, bool shift, bool alt, bool control)
 	public override void SendKeys (char keyChar, ConsoleKey consoleKey, bool shift, bool alt, bool control)
 	{
 	{
 		KeyCode key;
 		KeyCode key;
 
 
 		if (consoleKey == ConsoleKey.Packet) {
 		if (consoleKey == ConsoleKey.Packet) {
-			ConsoleModifiers mod = new ConsoleModifiers ();
+			var mod = new ConsoleModifiers ();
 			if (shift) {
 			if (shift) {
 				mod |= ConsoleModifiers.Shift;
 				mod |= ConsoleModifiers.Shift;
 			}
 			}
@@ -724,11 +712,9 @@ internal class CursesDriver : ConsoleDriver {
 			if (control) {
 			if (control) {
 				mod |= ConsoleModifiers.Control;
 				mod |= ConsoleModifiers.Control;
 			}
 			}
-			var cKeyInfo = ConsoleKeyMapping.GetConsoleKeyFromKey (keyChar, mod, out _);
-			key = ConsoleKeyMapping.MapConsoleKeyToKey ((ConsoleKey)cKeyInfo.Key, out bool mappable);
-			if (mappable) {
-				key = (KeyCode)cKeyInfo.KeyChar;
-			}
+			var cKeyInfo = new ConsoleKeyInfo (keyChar, consoleKey, shift, alt, control);
+			cKeyInfo = ConsoleKeyMapping.DecodeVKPacketToKConsoleKeyInfo (cKeyInfo);
+			key = ConsoleKeyMapping.MapConsoleKeyInfoToKeyCode (cKeyInfo);
 		} else {
 		} else {
 			key = (KeyCode)keyChar;
 			key = (KeyCode)keyChar;
 		}
 		}
@@ -737,16 +723,14 @@ internal class CursesDriver : ConsoleDriver {
 		OnKeyUp (new Key (key));
 		OnKeyUp (new Key (key));
 		//OnKeyPressed (new KeyEventArgsEventArgs (key));
 		//OnKeyPressed (new KeyEventArgsEventArgs (key));
 	}
 	}
-
-
 }
 }
 
 
-internal static class Platform {
+static class Platform {
 	[DllImport ("libc")]
 	[DllImport ("libc")]
-	static extern int uname (IntPtr buf);
+	extern static int uname (IntPtr buf);
 
 
 	[DllImport ("libc")]
 	[DllImport ("libc")]
-	static extern int killpg (int pgrp, int pid);
+	extern static int killpg (int pgrp, int pid);
 
 
 	static int _suspendSignal;
 	static int _suspendSignal;
 
 
@@ -793,7 +777,7 @@ internal static class Platform {
 	/// Suspends the process by sending SIGTSTP to itself
 	/// Suspends the process by sending SIGTSTP to itself
 	/// </summary>
 	/// </summary>
 	/// <returns>The suspend.</returns>
 	/// <returns>The suspend.</returns>
-	static public bool Suspend ()
+	public static bool Suspend ()
 	{
 	{
 		int signal = GetSuspendSignal ();
 		int signal = GetSuspendSignal ();
 		if (signal == -1) {
 		if (signal == -1) {

+ 1 - 1
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs

@@ -800,7 +800,7 @@ public static class FakeConsole {
 	{
 	{
 		MockKeyPresses.Push (new ConsoleKeyInfo (
 		MockKeyPresses.Push (new ConsoleKeyInfo (
 			(char)(key & ~KeyCode.CtrlMask & ~KeyCode.ShiftMask & ~KeyCode.AltMask),
 			(char)(key & ~KeyCode.CtrlMask & ~KeyCode.ShiftMask & ~KeyCode.AltMask),
-			ConsoleKeyMapping.GetConsoleKeyFromKey (key).Key,
+			ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (key).Key,
 			key.HasFlag (KeyCode.ShiftMask),
 			key.HasFlag (KeyCode.ShiftMask),
 			key.HasFlag (KeyCode.AltMask),
 			key.HasFlag (KeyCode.AltMask),
 			key.HasFlag (KeyCode.CtrlMask)));
 			key.HasFlag (KeyCode.CtrlMask)));

+ 22 - 22
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs

@@ -205,39 +205,39 @@ public class FakeDriver : ConsoleDriver {
 	{
 	{
 		switch (keyInfo.Key) {
 		switch (keyInfo.Key) {
 		case ConsoleKey.Escape:
 		case ConsoleKey.Escape:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.Esc);
+			return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Esc);
 		case ConsoleKey.Tab:
 		case ConsoleKey.Tab:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.Tab);
+			return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Tab);
 		case ConsoleKey.Clear:
 		case ConsoleKey.Clear:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.Clear);
+			return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Clear);
 		case ConsoleKey.Home:
 		case ConsoleKey.Home:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.Home);
+			return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Home);
 		case ConsoleKey.End:
 		case ConsoleKey.End:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.End);
+			return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.End);
 		case ConsoleKey.LeftArrow:
 		case ConsoleKey.LeftArrow:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.CursorLeft);
+			return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.CursorLeft);
 		case ConsoleKey.RightArrow:
 		case ConsoleKey.RightArrow:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.CursorRight);
+			return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.CursorRight);
 		case ConsoleKey.UpArrow:
 		case ConsoleKey.UpArrow:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.CursorUp);
+			return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.CursorUp);
 		case ConsoleKey.DownArrow:
 		case ConsoleKey.DownArrow:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.CursorDown);
+			return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.CursorDown);
 		case ConsoleKey.PageUp:
 		case ConsoleKey.PageUp:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.PageUp);
+			return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.PageUp);
 		case ConsoleKey.PageDown:
 		case ConsoleKey.PageDown:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.PageDown);
+			return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.PageDown);
 		case ConsoleKey.Enter:
 		case ConsoleKey.Enter:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.Enter);
+			return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Enter);
 		case ConsoleKey.Spacebar:
 		case ConsoleKey.Spacebar:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? KeyCode.Space : (KeyCode)keyInfo.KeyChar);
+			return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, keyInfo.KeyChar == 0 ? KeyCode.Space : (KeyCode)keyInfo.KeyChar);
 		case ConsoleKey.Backspace:
 		case ConsoleKey.Backspace:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.Backspace);
+			return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Backspace);
 		case ConsoleKey.Delete:
 		case ConsoleKey.Delete:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.DeleteChar);
+			return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Delete);
 		case ConsoleKey.Insert:
 		case ConsoleKey.Insert:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.InsertChar);
+			return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Insert);
 		case ConsoleKey.PrintScreen:
 		case ConsoleKey.PrintScreen:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.PrintScreen);
+			return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.PrintScreen);
 
 
 		case ConsoleKey.Oem1:
 		case ConsoleKey.Oem1:
 		case ConsoleKey.Oem2:
 		case ConsoleKey.Oem2:
@@ -256,25 +256,25 @@ public class FakeDriver : ConsoleDriver {
 				return KeyCode.Null;
 				return KeyCode.Null;
 			}
 			}
 
 
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, (KeyCode)((uint)keyInfo.KeyChar));
+			return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)((uint)keyInfo.KeyChar));
 		}
 		}
 
 
 		var key = keyInfo.Key;
 		var key = keyInfo.Key;
 		if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
 		if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
 			var delta = key - ConsoleKey.A;
 			var delta = key - ConsoleKey.A;
 			if (keyInfo.KeyChar != (uint)key) {
 			if (keyInfo.KeyChar != (uint)key) {
-				return ConsoleKeyMapping.MapKeyModifiers (keyInfo, (KeyCode)keyInfo.KeyChar);
+				return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.KeyChar);
 			}
 			}
 			if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control)
 			if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control)
 			|| keyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt)
 			|| keyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt)
 			|| keyInfo.Modifiers.HasFlag (ConsoleModifiers.Shift)) {
 			|| keyInfo.Modifiers.HasFlag (ConsoleModifiers.Shift)) {
-				return ConsoleKeyMapping.MapKeyModifiers (keyInfo, (KeyCode)((uint)KeyCode.A + delta));
+				return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)((uint)KeyCode.A + delta));
 			}
 			}
 			var alphaBase = ((keyInfo.Modifiers != ConsoleModifiers.Shift)) ? 'A' : 'a';
 			var alphaBase = ((keyInfo.Modifiers != ConsoleModifiers.Shift)) ? 'A' : 'a';
 			return (KeyCode)((uint)alphaBase + delta);
 			return (KeyCode)((uint)alphaBase + delta);
 		}
 		}
 
 
-		return ConsoleKeyMapping.MapKeyModifiers (keyInfo, (KeyCode)((uint)keyInfo.KeyChar));
+		return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)((uint)keyInfo.KeyChar));
 	}
 	}
 
 
 	private CursorVisibility _savedCursorVisibility;
 	private CursorVisibility _savedCursorVisibility;
@@ -282,7 +282,7 @@ public class FakeDriver : ConsoleDriver {
 	void MockKeyPressedHandler (ConsoleKeyInfo consoleKeyInfo)
 	void MockKeyPressedHandler (ConsoleKeyInfo consoleKeyInfo)
 	{
 	{
 		if (consoleKeyInfo.Key == ConsoleKey.Packet) {
 		if (consoleKeyInfo.Key == ConsoleKey.Packet) {
-			consoleKeyInfo = ConsoleKeyMapping.FromVKPacketToKConsoleKeyInfo (consoleKeyInfo);
+			consoleKeyInfo = ConsoleKeyMapping.DecodeVKPacketToKConsoleKeyInfo (consoleKeyInfo);
 		}
 		}
 
 
 		var map = MapKey (consoleKeyInfo);
 		var map = MapKey (consoleKeyInfo);

+ 298 - 319
Terminal.Gui/ConsoleDrivers/NetDriver.cs

@@ -7,13 +7,15 @@ using System.Diagnostics;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
+using System.Text;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
-using System.Text;
-using Terminal.Gui.ConsoleDrivers;
+using static Terminal.Gui.ConsoleDrivers.ConsoleKeyMapping;
 using static Terminal.Gui.NetEvents;
 using static Terminal.Gui.NetEvents;
+using static Terminal.Gui.WindowsConsole;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
+
 class NetWinVTConsole {
 class NetWinVTConsole {
 	IntPtr _inputHandle, _outputHandle, _errorHandle;
 	IntPtr _inputHandle, _outputHandle, _errorHandle;
 	uint _originalInputConsoleMode, _originalOutputConsoleMode, _originalErrorConsoleMode;
 	uint _originalInputConsoleMode, _originalOutputConsoleMode, _originalErrorConsoleMode;
@@ -49,7 +51,7 @@ class NetWinVTConsole {
 			throw new ApplicationException ($"Failed to get error console mode, error code: {GetLastError ()}.");
 			throw new ApplicationException ($"Failed to get error console mode, error code: {GetLastError ()}.");
 		}
 		}
 		_originalErrorConsoleMode = mode;
 		_originalErrorConsoleMode = mode;
-		if ((mode & (DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) {
+		if ((mode & DISABLE_NEWLINE_AUTO_RETURN) < DISABLE_NEWLINE_AUTO_RETURN) {
 			mode |= DISABLE_NEWLINE_AUTO_RETURN;
 			mode |= DISABLE_NEWLINE_AUTO_RETURN;
 			if (!SetConsoleMode (_errorHandle, mode)) {
 			if (!SetConsoleMode (_errorHandle, mode)) {
 				throw new ApplicationException ($"Failed to set error console mode, error code: {GetLastError ()}.");
 				throw new ApplicationException ($"Failed to set error console mode, error code: {GetLastError ()}.");
@@ -93,28 +95,28 @@ class NetWinVTConsole {
 	const uint ENABLE_LVB_GRID_WORLDWIDE = 10;
 	const uint ENABLE_LVB_GRID_WORLDWIDE = 10;
 
 
 	[DllImport ("kernel32.dll", SetLastError = true)]
 	[DllImport ("kernel32.dll", SetLastError = true)]
-	static extern IntPtr GetStdHandle (int nStdHandle);
+	extern static IntPtr GetStdHandle (int nStdHandle);
 
 
 	[DllImport ("kernel32.dll")]
 	[DllImport ("kernel32.dll")]
-	static extern bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode);
+	extern static bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode);
 
 
 	[DllImport ("kernel32.dll")]
 	[DllImport ("kernel32.dll")]
-	static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode);
+	extern static bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode);
 
 
 	[DllImport ("kernel32.dll")]
 	[DllImport ("kernel32.dll")]
-	static extern uint GetLastError ();
+	extern static uint GetLastError ();
 }
 }
 
 
-internal class NetEvents : IDisposable {
-	readonly ManualResetEventSlim _inputReady = new ManualResetEventSlim (false);
+class NetEvents : IDisposable {
+	readonly ManualResetEventSlim _inputReady = new (false);
 	CancellationTokenSource _inputReadyCancellationTokenSource;
 	CancellationTokenSource _inputReadyCancellationTokenSource;
 
 
-	readonly ManualResetEventSlim _waitForStart = new ManualResetEventSlim (false);
+	readonly ManualResetEventSlim _waitForStart = new (false);
 	//CancellationTokenSource _waitForStartCancellationTokenSource;
 	//CancellationTokenSource _waitForStartCancellationTokenSource;
 
 
-	readonly ManualResetEventSlim _winChange = new ManualResetEventSlim (false);
+	readonly ManualResetEventSlim _winChange = new (false);
 
 
-	readonly Queue<InputResult?> _inputQueue = new Queue<InputResult?> ();
+	readonly Queue<InputResult?> _inputQueue = new ();
 
 
 	readonly ConsoleDriver _consoleDriver;
 	readonly ConsoleDriver _consoleDriver;
 	ConsoleKeyInfo [] _cki;
 	ConsoleKeyInfo [] _cki;
@@ -123,7 +125,7 @@ internal class NetEvents : IDisposable {
 #if PROCESS_REQUEST
 #if PROCESS_REQUEST
 		bool _neededProcessRequest;
 		bool _neededProcessRequest;
 #endif
 #endif
-	public EscSeqRequests EscSeqRequests { get; } = new EscSeqRequests ();
+	public EscSeqRequests EscSeqRequests { get; } = new ();
 
 
 	public NetEvents (ConsoleDriver consoleDriver)
 	public NetEvents (ConsoleDriver consoleDriver)
 	{
 	{
@@ -209,17 +211,19 @@ internal class NetEvents : IDisposable {
 					} catch (OperationCanceledException) {
 					} catch (OperationCanceledException) {
 						return;
 						return;
 					}
 					}
-					if ((consoleKeyInfo.KeyChar == (char)KeyCode.Esc && !_isEscSeq)
-						|| (consoleKeyInfo.KeyChar != (char)KeyCode.Esc && _isEscSeq)) {
+					if (consoleKeyInfo.KeyChar == (char)KeyCode.Esc && !_isEscSeq
+					|| consoleKeyInfo.KeyChar != (char)KeyCode.Esc && _isEscSeq) {
 
 
 						if (_cki == null && consoleKeyInfo.KeyChar != (char)KeyCode.Esc && _isEscSeq) {
 						if (_cki == null && consoleKeyInfo.KeyChar != (char)KeyCode.Esc && _isEscSeq) {
 							_cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)KeyCode.Esc, 0,
 							_cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)KeyCode.Esc, 0,
-							    false, false, false), _cki);
+								false, false, false), _cki);
 						}
 						}
 						_isEscSeq = true;
 						_isEscSeq = true;
 						newConsoleKeyInfo = consoleKeyInfo;
 						newConsoleKeyInfo = consoleKeyInfo;
 						_cki = EscSeqUtils.ResizeArray (consoleKeyInfo, _cki);
 						_cki = EscSeqUtils.ResizeArray (consoleKeyInfo, _cki);
-						if (Console.KeyAvailable) continue;
+						if (Console.KeyAvailable) {
+							continue;
+						}
 						ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod);
 						ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod);
 						_cki = null;
 						_cki = null;
 						_isEscSeq = false;
 						_isEscSeq = false;
@@ -270,10 +274,10 @@ internal class NetEvents : IDisposable {
 					buffWidth = _consoleDriver.Cols;
 					buffWidth = _consoleDriver.Cols;
 				}
 				}
 				if (EnqueueWindowSizeEvent (
 				if (EnqueueWindowSizeEvent (
-				    Math.Max (Console.WindowHeight, 0),
-				    Math.Max (Console.WindowWidth, 0),
-				    buffHeight,
-				    buffWidth)) {
+					Math.Max (Console.WindowHeight, 0),
+					Math.Max (Console.WindowWidth, 0),
+					buffHeight,
+					buffWidth)) {
 
 
 					return;
 					return;
 				}
 				}
@@ -306,9 +310,11 @@ internal class NetEvents : IDisposable {
 	/// <returns></returns>
 	/// <returns></returns>
 	bool EnqueueWindowSizeEvent (int winHeight, int winWidth, int buffHeight, int buffWidth)
 	bool EnqueueWindowSizeEvent (int winHeight, int winWidth, int buffHeight, int buffWidth)
 	{
 	{
-		if (winWidth == _consoleDriver.Cols && winHeight == _consoleDriver.Rows) return false;
-		var w = Math.Max (winWidth, 0);
-		var h = Math.Max (winHeight, 0);
+		if (winWidth == _consoleDriver.Cols && winHeight == _consoleDriver.Rows) {
+			return false;
+		}
+		int w = Math.Max (winWidth, 0);
+		int h = Math.Max (winHeight, 0);
 		_inputQueue.Enqueue (new InputResult () {
 		_inputQueue.Enqueue (new InputResult () {
 			EventType = EventType.WindowSize,
 			EventType = EventType.WindowSize,
 			WindowSizeEvent = new WindowSizeEvent () {
 			WindowSizeEvent = new WindowSizeEvent () {
@@ -323,10 +329,10 @@ internal class NetEvents : IDisposable {
 	{
 	{
 		// isMouse is true if it's CSI<, false otherwise
 		// isMouse is true if it's CSI<, false otherwise
 		EscSeqUtils.DecodeEscSeq (EscSeqRequests, ref newConsoleKeyInfo, ref key, cki, ref mod,
 		EscSeqUtils.DecodeEscSeq (EscSeqRequests, ref newConsoleKeyInfo, ref key, cki, ref mod,
-		    out var c1Control, out var code, out var values, out var terminating,
-		    out var isMouse, out var mouseFlags,
-		    out var pos, out var isReq,
-		    (f, p) => HandleMouseEvent (MapMouseFlags (f), p));
+			out string c1Control, out string code, out string [] values, out string terminating,
+			out bool isMouse, out var mouseFlags,
+			out var pos, out bool isReq,
+			(f, p) => HandleMouseEvent (MapMouseFlags (f), p));
 
 
 		if (isMouse) {
 		if (isMouse) {
 			foreach (var mf in mouseFlags) {
 			foreach (var mf in mouseFlags) {
@@ -343,7 +349,7 @@ internal class NetEvents : IDisposable {
 	MouseButtonState MapMouseFlags (MouseFlags mouseFlags)
 	MouseButtonState MapMouseFlags (MouseFlags mouseFlags)
 	{
 	{
 		MouseButtonState mbs = default;
 		MouseButtonState mbs = default;
-		foreach (var flag in Enum.GetValues (mouseFlags.GetType ())) {
+		foreach (object flag in Enum.GetValues (mouseFlags.GetType ())) {
 			if (mouseFlags.HasFlag ((MouseFlags)flag)) {
 			if (mouseFlags.HasFlag ((MouseFlags)flag)) {
 				switch (flag) {
 				switch (flag) {
 				case MouseFlags.Button1Pressed:
 				case MouseFlags.Button1Pressed:
@@ -446,7 +452,7 @@ internal class NetEvents : IDisposable {
 		switch (terminating) {
 		switch (terminating) {
 		// BUGBUG: I can't find where we send a request for cursor position (ESC[?6n), so I'm not sure if this is needed.
 		// BUGBUG: I can't find where we send a request for cursor position (ESC[?6n), so I'm not sure if this is needed.
 		case EscSeqUtils.CSI_RequestCursorPositionReport_Terminator:
 		case EscSeqUtils.CSI_RequestCursorPositionReport_Terminator:
-			Point point = new Point {
+			var point = new Point {
 				X = int.Parse (values [1]) - 1,
 				X = int.Parse (values [1]) - 1,
 				Y = int.Parse (values [0]) - 1
 				Y = int.Parse (values [0]) - 1
 			};
 			};
@@ -469,10 +475,10 @@ internal class NetEvents : IDisposable {
 			switch (values [0]) {
 			switch (values [0]) {
 			case EscSeqUtils.CSI_ReportTerminalSizeInChars_ResponseValue:
 			case EscSeqUtils.CSI_ReportTerminalSizeInChars_ResponseValue:
 				EnqueueWindowSizeEvent (
 				EnqueueWindowSizeEvent (
-				    Math.Max (int.Parse (values [1]), 0),
-				    Math.Max (int.Parse (values [2]), 0),
-				    Math.Max (int.Parse (values [1]), 0),
-				    Math.Max (int.Parse (values [2]), 0));
+					Math.Max (int.Parse (values [1]), 0),
+					Math.Max (int.Parse (values [2]), 0),
+					Math.Max (int.Parse (values [1]), 0),
+					Math.Max (int.Parse (values [2]), 0));
 				break;
 				break;
 			default:
 			default:
 				EnqueueRequestResponseEvent (c1Control, code, values, terminating);
 				EnqueueRequestResponseEvent (c1Control, code, values, terminating);
@@ -489,7 +495,7 @@ internal class NetEvents : IDisposable {
 
 
 	void EnqueueRequestResponseEvent (string c1Control, string code, string [] values, string terminating)
 	void EnqueueRequestResponseEvent (string c1Control, string code, string [] values, string terminating)
 	{
 	{
-		EventType eventType = EventType.RequestResponse;
+		var eventType = EventType.RequestResponse;
 		var requestRespEv = new RequestResponseEvent () {
 		var requestRespEv = new RequestResponseEvent () {
 			ResultTuple = (c1Control, code, values, terminating)
 			ResultTuple = (c1Control, code, values, terminating)
 		};
 		};
@@ -501,9 +507,9 @@ internal class NetEvents : IDisposable {
 
 
 	void HandleMouseEvent (MouseButtonState buttonState, Point pos)
 	void HandleMouseEvent (MouseButtonState buttonState, Point pos)
 	{
 	{
-		MouseEvent mouseEvent = new MouseEvent () {
+		var mouseEvent = new MouseEvent () {
 			Position = pos,
 			Position = pos,
-			ButtonState = buttonState,
+			ButtonState = buttonState
 		};
 		};
 
 
 		_inputQueue.Enqueue (new InputResult () {
 		_inputQueue.Enqueue (new InputResult () {
@@ -581,11 +587,40 @@ internal class NetEvents : IDisposable {
 		public WindowSizeEvent WindowSizeEvent;
 		public WindowSizeEvent WindowSizeEvent;
 		public WindowPositionEvent WindowPositionEvent;
 		public WindowPositionEvent WindowPositionEvent;
 		public RequestResponseEvent RequestResponseEvent;
 		public RequestResponseEvent RequestResponseEvent;
+
+		public override readonly string ToString ()
+		{
+			return EventType switch {
+				EventType.Key => ToString (ConsoleKeyInfo),
+				EventType.Mouse => MouseEvent.ToString (),
+				//EventType.WindowSize => WindowSize.ToString (),
+				//EventType.RequestResponse => RequestResponse.ToString (),
+				_ => "Unknown event type: " + EventType
+			};
+		}
+
+		/// <summary>
+		/// Prints a ConsoleKeyInfoEx structure
+		/// </summary>
+		/// <param name="cki"></param>
+		/// <returns></returns>
+		public readonly string ToString (ConsoleKeyInfo cki)
+		{
+			var ke = new Key ((KeyCode)cki.KeyChar);
+			var sb = new StringBuilder ();
+			sb.Append ($"Key: {(KeyCode)cki.Key} ({cki.Key})");
+			sb.Append ((cki.Modifiers & ConsoleModifiers.Shift) != 0 ? " | Shift" : string.Empty);
+			sb.Append ((cki.Modifiers & ConsoleModifiers.Control) != 0 ? " | Control" : string.Empty);
+			sb.Append ((cki.Modifiers & ConsoleModifiers.Alt) != 0 ? " | Alt" : string.Empty);
+			sb.Append ($", KeyChar: {ke.AsRune.MakePrintable ()} ({(uint)cki.KeyChar}) ");
+			var s = sb.ToString ().TrimEnd (',').TrimEnd (' ');
+			return $"[ConsoleKeyInfo({s})]";
+		}
 	}
 	}
 
 
 	void HandleKeyboardEvent (ConsoleKeyInfo cki)
 	void HandleKeyboardEvent (ConsoleKeyInfo cki)
 	{
 	{
-		InputResult inputResult = new InputResult {
+		var inputResult = new InputResult {
 			EventType = EventType.Key,
 			EventType = EventType.Key,
 			ConsoleKeyInfo = cki
 			ConsoleKeyInfo = cki
 		};
 		};
@@ -611,7 +646,7 @@ internal class NetEvents : IDisposable {
 	}
 	}
 }
 }
 
 
-internal class NetDriver : ConsoleDriver {
+class NetDriver : ConsoleDriver {
 	const int COLOR_BLACK = 30;
 	const int COLOR_BLACK = 30;
 	const int COLOR_RED = 31;
 	const int COLOR_RED = 31;
 	const int COLOR_GREEN = 32;
 	const int COLOR_GREEN = 32;
@@ -631,9 +666,10 @@ internal class NetDriver : ConsoleDriver {
 
 
 	NetMainLoop _mainLoopDriver = null;
 	NetMainLoop _mainLoopDriver = null;
 
 
-	public override bool SupportsTrueColor => Environment.OSVersion.Platform == PlatformID.Unix || (IsWinPlatform && Environment.OSVersion.Version.Build >= 14931);
+	public override bool SupportsTrueColor => Environment.OSVersion.Platform == PlatformID.Unix || IsWinPlatform && Environment.OSVersion.Version.Build >= 14931;
 
 
 	public NetWinVTConsole NetWinConsole { get; private set; }
 	public NetWinVTConsole NetWinConsole { get; private set; }
+
 	public bool IsWinPlatform { get; private set; }
 	public bool IsWinPlatform { get; private set; }
 
 
 	internal override MainLoop Init ()
 	internal override MainLoop Init ()
@@ -709,6 +745,21 @@ internal class NetDriver : ConsoleDriver {
 		}
 		}
 	}
 	}
 
 
+
+	#region Size and Position Handling
+	volatile bool _winSizeChanging;
+
+	void SetWindowPosition (int col, int row)
+	{
+		if (!RunningUnitTests) {
+			Top = Console.WindowTop;
+			Left = Console.WindowLeft;
+		} else {
+			Top = row;
+			Left = col;
+		}
+	}
+
 	public virtual void ResizeScreen ()
 	public virtual void ResizeScreen ()
 	{
 	{
 		// Not supported on Unix.
 		// Not supported on Unix.
@@ -727,7 +778,7 @@ internal class NetDriver : ConsoleDriver {
 					Console.SetBufferSize (Cols, Rows);
 					Console.SetBufferSize (Cols, Rows);
 				}
 				}
 #pragma warning restore CA1416
 #pragma warning restore CA1416
-			} catch (System.IO.IOException) {
+			} catch (IOException) {
 				Clip = new Rect (0, 0, Cols, Rows);
 				Clip = new Rect (0, 0, Cols, Rows);
 			} catch (ArgumentOutOfRangeException) {
 			} catch (ArgumentOutOfRangeException) {
 				Clip = new Rect (0, 0, Cols, Rows);
 				Clip = new Rect (0, 0, Cols, Rows);
@@ -739,6 +790,7 @@ internal class NetDriver : ConsoleDriver {
 
 
 		Clip = new Rect (0, 0, Cols, Rows);
 		Clip = new Rect (0, 0, Cols, Rows);
 	}
 	}
+	#endregion
 
 
 	public override void Refresh ()
 	public override void Refresh ()
 	{
 	{
@@ -752,18 +804,18 @@ internal class NetDriver : ConsoleDriver {
 			return;
 			return;
 		}
 		}
 
 
-		var top = 0;
-		var left = 0;
-		var rows = Rows;
-		var cols = Cols;
-		System.Text.StringBuilder output = new System.Text.StringBuilder ();
-		Attribute redrawAttr = new Attribute ();
-		var lastCol = -1;
+		int top = 0;
+		int left = 0;
+		int rows = Rows;
+		int cols = Cols;
+		var output = new StringBuilder ();
+		var redrawAttr = new Attribute ();
+		int lastCol = -1;
 
 
 		var savedVisibitity = _cachedCursorVisibility;
 		var savedVisibitity = _cachedCursorVisibility;
 		SetCursorVisibility (CursorVisibility.Invisible);
 		SetCursorVisibility (CursorVisibility.Invisible);
 
 
-		for (var row = top; row < rows; row++) {
+		for (int row = top; row < rows; row++) {
 			if (Console.WindowHeight < 1) {
 			if (Console.WindowHeight < 1) {
 				return;
 				return;
 			}
 			}
@@ -775,9 +827,9 @@ internal class NetDriver : ConsoleDriver {
 			}
 			}
 			_dirtyLines [row] = false;
 			_dirtyLines [row] = false;
 			output.Clear ();
 			output.Clear ();
-			for (var col = left; col < cols; col++) {
+			for (int col = left; col < cols; col++) {
 				lastCol = -1;
 				lastCol = -1;
-				var outputWidth = 0;
+				int outputWidth = 0;
 				for (; col < cols; col++) {
 				for (; col < cols; col++) {
 					if (!Contents [row, col].IsDirty) {
 					if (!Contents [row, col].IsDirty) {
 						if (output.Length > 0) {
 						if (output.Length > 0) {
@@ -795,7 +847,7 @@ internal class NetDriver : ConsoleDriver {
 						lastCol = col;
 						lastCol = col;
 					}
 					}
 
 
-					Attribute attr = Contents [row, col].Attribute.Value;
+					var attr = Contents [row, col].Attribute.Value;
 					// Performance: Only send the escape sequence if the attribute has changed.
 					// Performance: Only send the escape sequence if the attribute has changed.
 					if (attr != redrawAttr) {
 					if (attr != redrawAttr) {
 						redrawAttr = attr;
 						redrawAttr = attr;
@@ -823,7 +875,7 @@ internal class NetDriver : ConsoleDriver {
 						//	output.Append (combMark);
 						//	output.Append (combMark);
 						//}
 						//}
 						// WriteToConsole (output, ref lastCol, row, ref outputWidth);
 						// WriteToConsole (output, ref lastCol, row, ref outputWidth);
-					} else if ((rune.IsSurrogatePair () && rune.GetColumns () < 2)) {
+					} else if (rune.IsSurrogatePair () && rune.GetColumns () < 2) {
 						WriteToConsole (output, ref lastCol, row, ref outputWidth);
 						WriteToConsole (output, ref lastCol, row, ref outputWidth);
 						SetCursorPosition (col - 1, row);
 						SetCursorPosition (col - 1, row);
 					}
 					}
@@ -850,37 +902,33 @@ internal class NetDriver : ConsoleDriver {
 	}
 	}
 
 
 	#region Color Handling
 	#region Color Handling
-
 	// Cache the list of ConsoleColor values.
 	// Cache the list of ConsoleColor values.
-	private static readonly HashSet<int> ConsoleColorValues = new HashSet<int> (
-	    Enum.GetValues (typeof (ConsoleColor)).OfType<ConsoleColor> ().Select (c => (int)c)
+	static readonly HashSet<int> ConsoleColorValues = new (
+		Enum.GetValues (typeof (ConsoleColor)).OfType<ConsoleColor> ().Select (c => (int)c)
 	);
 	);
 
 
 	// Dictionary for mapping ConsoleColor values to the values used by System.Net.Console.
 	// Dictionary for mapping ConsoleColor values to the values used by System.Net.Console.
-	private static Dictionary<ConsoleColor, int> colorMap = new Dictionary<ConsoleColor, int> {
-	{ ConsoleColor.Black, COLOR_BLACK },
-	{ ConsoleColor.DarkBlue, COLOR_BLUE },
-	{ ConsoleColor.DarkGreen, COLOR_GREEN },
-	{ ConsoleColor.DarkCyan, COLOR_CYAN },
-	{ ConsoleColor.DarkRed, COLOR_RED },
-	{ ConsoleColor.DarkMagenta, COLOR_MAGENTA },
-	{ ConsoleColor.DarkYellow, COLOR_YELLOW },
-	{ ConsoleColor.Gray, COLOR_WHITE },
-	{ ConsoleColor.DarkGray, COLOR_BRIGHT_BLACK },
-	{ ConsoleColor.Blue, COLOR_BRIGHT_BLUE },
-	{ ConsoleColor.Green, COLOR_BRIGHT_GREEN },
-	{ ConsoleColor.Cyan, COLOR_BRIGHT_CYAN },
-	{ ConsoleColor.Red, COLOR_BRIGHT_RED },
-	{ ConsoleColor.Magenta, COLOR_BRIGHT_MAGENTA },
-	{ ConsoleColor.Yellow, COLOR_BRIGHT_YELLOW },
-	{ ConsoleColor.White, COLOR_BRIGHT_WHITE }
-    };
+	static Dictionary<ConsoleColor, int> colorMap = new () {
+		{ ConsoleColor.Black, COLOR_BLACK },
+		{ ConsoleColor.DarkBlue, COLOR_BLUE },
+		{ ConsoleColor.DarkGreen, COLOR_GREEN },
+		{ ConsoleColor.DarkCyan, COLOR_CYAN },
+		{ ConsoleColor.DarkRed, COLOR_RED },
+		{ ConsoleColor.DarkMagenta, COLOR_MAGENTA },
+		{ ConsoleColor.DarkYellow, COLOR_YELLOW },
+		{ ConsoleColor.Gray, COLOR_WHITE },
+		{ ConsoleColor.DarkGray, COLOR_BRIGHT_BLACK },
+		{ ConsoleColor.Blue, COLOR_BRIGHT_BLUE },
+		{ ConsoleColor.Green, COLOR_BRIGHT_GREEN },
+		{ ConsoleColor.Cyan, COLOR_BRIGHT_CYAN },
+		{ ConsoleColor.Red, COLOR_BRIGHT_RED },
+		{ ConsoleColor.Magenta, COLOR_BRIGHT_MAGENTA },
+		{ ConsoleColor.Yellow, COLOR_BRIGHT_YELLOW },
+		{ ConsoleColor.White, COLOR_BRIGHT_WHITE }
+	};
 
 
 	// Map a ConsoleColor to a platform dependent value.
 	// Map a ConsoleColor to a platform dependent value.
-	int MapColors (ConsoleColor color, bool isForeground = true)
-	{
-		return colorMap.TryGetValue (color, out var colorValue) ? colorValue + (isForeground ? 0 : 10) : 0;
-	}
+	int MapColors (ConsoleColor color, bool isForeground = true) => colorMap.TryGetValue (color, out int colorValue) ? colorValue + (isForeground ? 0 : 10) : 0;
 
 
 	///// <remarks>
 	///// <remarks>
 	///// In the NetDriver, colors are encoded as an int. 
 	///// In the NetDriver, colors are encoded as an int. 
@@ -938,7 +986,7 @@ internal class NetDriver : ConsoleDriver {
 	public override bool SetCursorVisibility (CursorVisibility visibility)
 	public override bool SetCursorVisibility (CursorVisibility visibility)
 	{
 	{
 		_cachedCursorVisibility = visibility;
 		_cachedCursorVisibility = visibility;
-		var isVisible = RunningUnitTests ? visibility == CursorVisibility.Default : Console.CursorVisible = visibility == CursorVisibility.Default;
+		bool isVisible = RunningUnitTests ? visibility == CursorVisibility.Default : Console.CursorVisible = visibility == CursorVisibility.Default;
 		Console.Out.Write (isVisible ? EscSeqUtils.CSI_ShowCursor : EscSeqUtils.CSI_HideCursor);
 		Console.Out.Write (isVisible ? EscSeqUtils.CSI_ShowCursor : EscSeqUtils.CSI_HideCursor);
 		return isVisible;
 		return isVisible;
 	}
 	}
@@ -946,7 +994,7 @@ internal class NetDriver : ConsoleDriver {
 	public override bool EnsureCursorVisibility ()
 	public override bool EnsureCursorVisibility ()
 	{
 	{
 		if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows)) {
 		if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows)) {
-			GetCursorVisibility (out CursorVisibility cursorVisibility);
+			GetCursorVisibility (out var cursorVisibility);
 			_cachedCursorVisibility = cursorVisibility;
 			_cachedCursorVisibility = cursorVisibility;
 			SetCursorVisibility (CursorVisibility.Invisible);
 			SetCursorVisibility (CursorVisibility.Invisible);
 			return false;
 			return false;
@@ -957,22 +1005,7 @@ internal class NetDriver : ConsoleDriver {
 	}
 	}
 	#endregion
 	#endregion
 
 
-	#region Size and Position Handling
-
-	void SetWindowPosition (int col, int row)
-	{
-		if (!RunningUnitTests) {
-			Top = Console.WindowTop;
-			Left = Console.WindowLeft;
-		} else {
-			Top = row;
-			Left = col;
-		}
-	}
-
-	#endregion
-
-
+	#region Mouse Handling
 	public void StartReportingMouseMoves ()
 	public void StartReportingMouseMoves ()
 	{
 	{
 		if (!RunningUnitTests) {
 		if (!RunningUnitTests) {
@@ -987,6 +1020,106 @@ internal class NetDriver : ConsoleDriver {
 		}
 		}
 	}
 	}
 
 
+	MouseEvent ToDriverMouse (NetEvents.MouseEvent me)
+	{
+		//System.Diagnostics.Debug.WriteLine ($"X: {me.Position.X}; Y: {me.Position.Y}; ButtonState: {me.ButtonState}");
+
+		MouseFlags mouseFlag = 0;
+
+		if ((me.ButtonState & MouseButtonState.Button1Pressed) != 0) {
+			mouseFlag |= MouseFlags.Button1Pressed;
+		}
+		if ((me.ButtonState & MouseButtonState.Button1Released) != 0) {
+			mouseFlag |= MouseFlags.Button1Released;
+		}
+		if ((me.ButtonState & MouseButtonState.Button1Clicked) != 0) {
+			mouseFlag |= MouseFlags.Button1Clicked;
+		}
+		if ((me.ButtonState & MouseButtonState.Button1DoubleClicked) != 0) {
+			mouseFlag |= MouseFlags.Button1DoubleClicked;
+		}
+		if ((me.ButtonState & MouseButtonState.Button1TripleClicked) != 0) {
+			mouseFlag |= MouseFlags.Button1TripleClicked;
+		}
+		if ((me.ButtonState & MouseButtonState.Button2Pressed) != 0) {
+			mouseFlag |= MouseFlags.Button2Pressed;
+		}
+		if ((me.ButtonState & MouseButtonState.Button2Released) != 0) {
+			mouseFlag |= MouseFlags.Button2Released;
+		}
+		if ((me.ButtonState & MouseButtonState.Button2Clicked) != 0) {
+			mouseFlag |= MouseFlags.Button2Clicked;
+		}
+		if ((me.ButtonState & MouseButtonState.Button2DoubleClicked) != 0) {
+			mouseFlag |= MouseFlags.Button2DoubleClicked;
+		}
+		if ((me.ButtonState & MouseButtonState.Button2TripleClicked) != 0) {
+			mouseFlag |= MouseFlags.Button2TripleClicked;
+		}
+		if ((me.ButtonState & MouseButtonState.Button3Pressed) != 0) {
+			mouseFlag |= MouseFlags.Button3Pressed;
+		}
+		if ((me.ButtonState & MouseButtonState.Button3Released) != 0) {
+			mouseFlag |= MouseFlags.Button3Released;
+		}
+		if ((me.ButtonState & MouseButtonState.Button3Clicked) != 0) {
+			mouseFlag |= MouseFlags.Button3Clicked;
+		}
+		if ((me.ButtonState & MouseButtonState.Button3DoubleClicked) != 0) {
+			mouseFlag |= MouseFlags.Button3DoubleClicked;
+		}
+		if ((me.ButtonState & MouseButtonState.Button3TripleClicked) != 0) {
+			mouseFlag |= MouseFlags.Button3TripleClicked;
+		}
+		if ((me.ButtonState & MouseButtonState.ButtonWheeledUp) != 0) {
+			mouseFlag |= MouseFlags.WheeledUp;
+		}
+		if ((me.ButtonState & MouseButtonState.ButtonWheeledDown) != 0) {
+			mouseFlag |= MouseFlags.WheeledDown;
+		}
+		if ((me.ButtonState & MouseButtonState.ButtonWheeledLeft) != 0) {
+			mouseFlag |= MouseFlags.WheeledLeft;
+		}
+		if ((me.ButtonState & MouseButtonState.ButtonWheeledRight) != 0) {
+			mouseFlag |= MouseFlags.WheeledRight;
+		}
+		if ((me.ButtonState & MouseButtonState.Button4Pressed) != 0) {
+			mouseFlag |= MouseFlags.Button4Pressed;
+		}
+		if ((me.ButtonState & MouseButtonState.Button4Released) != 0) {
+			mouseFlag |= MouseFlags.Button4Released;
+		}
+		if ((me.ButtonState & MouseButtonState.Button4Clicked) != 0) {
+			mouseFlag |= MouseFlags.Button4Clicked;
+		}
+		if ((me.ButtonState & MouseButtonState.Button4DoubleClicked) != 0) {
+			mouseFlag |= MouseFlags.Button4DoubleClicked;
+		}
+		if ((me.ButtonState & MouseButtonState.Button4TripleClicked) != 0) {
+			mouseFlag |= MouseFlags.Button4TripleClicked;
+		}
+		if ((me.ButtonState & MouseButtonState.ReportMousePosition) != 0) {
+			mouseFlag |= MouseFlags.ReportMousePosition;
+		}
+		if ((me.ButtonState & MouseButtonState.ButtonShift) != 0) {
+			mouseFlag |= MouseFlags.ButtonShift;
+		}
+		if ((me.ButtonState & MouseButtonState.ButtonCtrl) != 0) {
+			mouseFlag |= MouseFlags.ButtonCtrl;
+		}
+		if ((me.ButtonState & MouseButtonState.ButtonAlt) != 0) {
+			mouseFlag |= MouseFlags.ButtonAlt;
+		}
+
+		return new MouseEvent () {
+			X = me.Position.X,
+			Y = me.Position.Y,
+			Flags = mouseFlag
+		};
+	}
+	#endregion Mouse Handling
+
+	#region Keyboard Handling
 	ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
 	ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
 	{
 	{
 		if (consoleKeyInfo.Key != ConsoleKey.Packet) {
 		if (consoleKeyInfo.Key != ConsoleKey.Packet) {
@@ -994,11 +1127,11 @@ internal class NetDriver : ConsoleDriver {
 		}
 		}
 
 
 		var mod = consoleKeyInfo.Modifiers;
 		var mod = consoleKeyInfo.Modifiers;
-		var shift = (mod & ConsoleModifiers.Shift) != 0;
-		var alt = (mod & ConsoleModifiers.Alt) != 0;
-		var control = (mod & ConsoleModifiers.Control) != 0;
+		bool shift = (mod & ConsoleModifiers.Shift) != 0;
+		bool alt = (mod & ConsoleModifiers.Alt) != 0;
+		bool control = (mod & ConsoleModifiers.Control) != 0;
 
 
-		var cKeyInfo = ConsoleKeyMapping.GetConsoleKeyFromKey (consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out _);
+		var cKeyInfo = DecodeVKPacketToKConsoleKeyInfo (consoleKeyInfo);
 
 
 		return new ConsoleKeyInfo (cKeyInfo.KeyChar, cKeyInfo.Key, shift, alt, control);
 		return new ConsoleKeyInfo (cKeyInfo.KeyChar, cKeyInfo.Key, shift, alt, control);
 	}
 	}
@@ -1006,37 +1139,11 @@ internal class NetDriver : ConsoleDriver {
 	KeyCode MapKey (ConsoleKeyInfo keyInfo)
 	KeyCode MapKey (ConsoleKeyInfo keyInfo)
 	{
 	{
 		switch (keyInfo.Key) {
 		switch (keyInfo.Key) {
-		case ConsoleKey.Escape:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.Esc);
-		case ConsoleKey.Tab:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.Tab);
-		case ConsoleKey.Home:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.Home);
-		case ConsoleKey.End:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.End);
-		case ConsoleKey.LeftArrow:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.CursorLeft);
-		case ConsoleKey.RightArrow:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.CursorRight);
-		case ConsoleKey.UpArrow:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.CursorUp);
-		case ConsoleKey.DownArrow:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.CursorDown);
-		case ConsoleKey.PageUp:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.PageUp);
-		case ConsoleKey.PageDown:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.PageDown);
-		case ConsoleKey.Enter:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.Enter);
-		case ConsoleKey.Spacebar:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? KeyCode.Space : (KeyCode)keyInfo.KeyChar);
-		case ConsoleKey.Backspace:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.Backspace);
-		case ConsoleKey.Delete:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.DeleteChar);
-		case ConsoleKey.Insert:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.InsertChar);
-
+		case ConsoleKey.OemPeriod:
+		case ConsoleKey.OemComma:
+		case ConsoleKey.OemPlus:
+		case ConsoleKey.OemMinus:
+		case ConsoleKey.Packet:
 		case ConsoleKey.Oem1:
 		case ConsoleKey.Oem1:
 		case ConsoleKey.Oem2:
 		case ConsoleKey.Oem2:
 		case ConsoleKey.Oem3:
 		case ConsoleKey.Oem3:
@@ -1046,99 +1153,78 @@ internal class NetDriver : ConsoleDriver {
 		case ConsoleKey.Oem7:
 		case ConsoleKey.Oem7:
 		case ConsoleKey.Oem8:
 		case ConsoleKey.Oem8:
 		case ConsoleKey.Oem102:
 		case ConsoleKey.Oem102:
-			var ret = ConsoleKeyMapping.MapKeyModifiers (keyInfo, (KeyCode)((uint)keyInfo.KeyChar));
-			if (ret.HasFlag (KeyCode.ShiftMask)) {
-				ret &= ~KeyCode.ShiftMask;
+			if (keyInfo.KeyChar == 0) {
+				// If the keyChar is 0, keyInfo.Key value is not a printable character. 
+
+				return KeyCode.Null;// MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode)keyInfo.Key);
+			} else {
+				if (keyInfo.Modifiers != ConsoleModifiers.Shift) {
+					// If Shift wasn't down we don't need to do anything but return the keyInfo.KeyChar
+					return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)(keyInfo.KeyChar));
+				}
+
+				// Strip off Shift - We got here because they KeyChar from Windows is the shifted char (e.g. "Ç")
+				// and passing on Shift would be redundant.
+				return MapToKeyCodeModifiers (keyInfo.Modifiers & ~ConsoleModifiers.Shift, (KeyCode)keyInfo.KeyChar);
 			}
 			}
-			return ret;
+			break;
 
 
-		case ConsoleKey.OemPeriod:
-		case ConsoleKey.OemComma:
-		case ConsoleKey.OemPlus:
-		case ConsoleKey.OemMinus:
-			return (KeyCode)((uint)keyInfo.KeyChar);
+
+			return (KeyCode)(uint)keyInfo.KeyChar;
 		}
 		}
 
 
 		var key = keyInfo.Key;
 		var key = keyInfo.Key;
-		if (key is >= ConsoleKey.A and <= ConsoleKey.Z) {
-			var delta = key - ConsoleKey.A;
-			if (keyInfo.Modifiers == ConsoleModifiers.Control) {
-				return (KeyCode)(((uint)KeyCode.CtrlMask) | ((uint)KeyCode.A + delta));
-			}
-			if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
-				return (KeyCode)(((uint)KeyCode.AltMask) | ((uint)KeyCode.A + delta));
-			}
-			if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) {
-				return ConsoleKeyMapping.MapKeyModifiers (keyInfo, (KeyCode)((uint)KeyCode.A + delta));
-			}
-			if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
-				if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) {
-					return ConsoleKeyMapping.MapKeyModifiers (keyInfo, (KeyCode)((uint)KeyCode.A + delta));
-				}
+		// A..Z are special cased:
+		// - Alone, they represent lowercase a...z
+		// - With ShiftMask they are A..Z
+		// - If CapsLock is on the above is reversed.
+		// - If Alt and/or Ctrl are present, treat as upper case
+		if (keyInfo.Key is >= ConsoleKey.A and <= ConsoleKey.Z) {
+			if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt) || keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control)) {
+				return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)(uint)keyInfo.Key);
 			}
 			}
 
 
-			if (((keyInfo.Modifiers == ConsoleModifiers.Shift) /*^ (keyInfoEx.CapsLock)*/)) {
-				if (keyInfo.KeyChar <= (uint)KeyCode.Z) {
-					return (KeyCode)((uint)KeyCode.A + delta) | KeyCode.ShiftMask;
+			if (keyInfo.Modifiers == ConsoleModifiers.Shift) {
+				// If ShiftMask is on  add the ShiftMask
+				if (char.IsUpper (keyInfo.KeyChar)) {
+					return (KeyCode)((uint)keyInfo.Key) | KeyCode.ShiftMask;
 				}
 				}
 			}
 			}
-			// This is buggy because is converting a lower case to a upper case and mustn't
-			//if (((KeyCode)((uint)keyInfo.KeyChar) & KeyCode.Space) == KeyCode.Space) {
-			//	return (KeyCode)((uint)keyInfo.KeyChar) & ~KeyCode.Space;
-			//}
 			return (KeyCode)(uint)keyInfo.KeyChar;
 			return (KeyCode)(uint)keyInfo.KeyChar;
 		}
 		}
-		if (key is >= ConsoleKey.D0 and <= ConsoleKey.D9) {
-			var delta = key - ConsoleKey.D0;
-			if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
-				return (KeyCode)(((uint)KeyCode.AltMask) | ((uint)KeyCode.D0 + delta));
-			}
-			if (keyInfo.Modifiers == ConsoleModifiers.Control) {
-				return (KeyCode)(((uint)KeyCode.CtrlMask) | ((uint)KeyCode.D0 + delta));
-			}
-			if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
-				if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)KeyCode.D0 + delta)) {
-					return ConsoleKeyMapping.MapKeyModifiers (keyInfo, (KeyCode)((uint)KeyCode.D0 + delta));
-				}
-			}
-			return (KeyCode)((uint)keyInfo.KeyChar);
-		}
-		if (key is >= ConsoleKey.F1 and <= ConsoleKey.F12) {
-			var delta = key - ConsoleKey.F1;
-			if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
-				return ConsoleKeyMapping.MapKeyModifiers (keyInfo, (KeyCode)((uint)KeyCode.F1 + delta));
-			}
 
 
-			return (KeyCode)((uint)KeyCode.F1 + delta);
+		// Handle control keys whose VK codes match the related ASCII value (those below ASCII 33) like ESC
+		if (keyInfo.Key != ConsoleKey.None && Enum.IsDefined (typeof (KeyCode), (uint)keyInfo.Key)) {
+			return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)(keyInfo.Key));
 		}
 		}
 
 
-		// Is it a key between a..z?
-		if ((char)keyInfo.KeyChar is >= 'a' and <= 'z') {
-			// 'a' should be Key.A
-			return (KeyCode)((uint)keyInfo.KeyChar) & ~KeyCode.Space;
+		// Handle control keys (e.g. CursorUp)
+		if (keyInfo.Key != ConsoleKey.None && Enum.IsDefined (typeof (KeyCode), ((uint)keyInfo.Key + (uint)KeyCode.MaxCodePoint))) {
+			return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)((uint)keyInfo.Key + (uint)KeyCode.MaxCodePoint));
 		}
 		}
 
 
-		// Is it a key between A..Z?
-		if (((KeyCode)((uint)keyInfo.KeyChar) & ~KeyCode.Space) is >= KeyCode.A and <= KeyCode.Z) {
-			// It's Key.A...Z.  Make it Key.A | Key.ShiftMask
-			return (KeyCode)((uint)keyInfo.KeyChar) & ~KeyCode.Space | KeyCode.ShiftMask;
-		}
 
 
 		return (KeyCode)(uint)keyInfo.KeyChar;
 		return (KeyCode)(uint)keyInfo.KeyChar;
 	}
 	}
+	#endregion Keyboard Handling
 
 
-	volatile bool _winSizeChanging;
-
-	void ProcessInput (NetEvents.InputResult inputEvent)
+	void ProcessInput (InputResult inputEvent)
 	{
 	{
 		switch (inputEvent.EventType) {
 		switch (inputEvent.EventType) {
 		case NetEvents.EventType.Key:
 		case NetEvents.EventType.Key:
-			ConsoleKeyInfo consoleKeyInfo = inputEvent.ConsoleKeyInfo;
-			if (consoleKeyInfo.Key == ConsoleKey.Packet) {
-				consoleKeyInfo = FromVKPacketToKConsoleKeyInfo (consoleKeyInfo);
-			}
+			var consoleKeyInfo = inputEvent.ConsoleKeyInfo;
+			//if (consoleKeyInfo.Key == ConsoleKey.Packet) {
+			//	consoleKeyInfo = FromVKPacketToKConsoleKeyInfo (consoleKeyInfo);
+			//}
+
+			//Debug.WriteLine ($"event: {inputEvent}");
+
 			var map = MapKey (consoleKeyInfo);
 			var map = MapKey (consoleKeyInfo);
 
 
+			if (map == KeyCode.Null) {
+				break;
+			}
+
 			OnKeyDown (new Key (map));
 			OnKeyDown (new Key (map));
 			OnKeyUp (new Key (map));
 			OnKeyUp (new Key (map));
 			break;
 			break;
@@ -1150,16 +1236,14 @@ internal class NetDriver : ConsoleDriver {
 			Top = 0;
 			Top = 0;
 			Left = 0;
 			Left = 0;
 			Cols = inputEvent.WindowSizeEvent.Size.Width;
 			Cols = inputEvent.WindowSizeEvent.Size.Width;
-			Rows = Math.Max (inputEvent.WindowSizeEvent.Size.Height, 0); ;
+			Rows = Math.Max (inputEvent.WindowSizeEvent.Size.Height, 0);
+			;
 			ResizeScreen ();
 			ResizeScreen ();
 			ClearContents ();
 			ClearContents ();
 			_winSizeChanging = false;
 			_winSizeChanging = false;
 			OnSizeChanged (new SizeChangedEventArgs (new Size (Cols, Rows)));
 			OnSizeChanged (new SizeChangedEventArgs (new Size (Cols, Rows)));
 			break;
 			break;
 		case NetEvents.EventType.RequestResponse:
 		case NetEvents.EventType.RequestResponse:
-			// BUGBUG: What is this for? It does not seem to be used anywhere. 
-			// It is also not clear what it does. View.Data is documented as "This property is not used internally"
-			Application.Top.Data = inputEvent.RequestResponseEvent.ResultTuple;
 			break;
 			break;
 		case NetEvents.EventType.WindowPosition:
 		case NetEvents.EventType.WindowPosition:
 			break;
 			break;
@@ -1168,107 +1252,9 @@ internal class NetDriver : ConsoleDriver {
 		}
 		}
 	}
 	}
 
 
-	MouseEvent ToDriverMouse (NetEvents.MouseEvent me)
-	{
-		//System.Diagnostics.Debug.WriteLine ($"X: {me.Position.X}; Y: {me.Position.Y}; ButtonState: {me.ButtonState}");
-
-		MouseFlags mouseFlag = 0;
-
-		if ((me.ButtonState & NetEvents.MouseButtonState.Button1Pressed) != 0) {
-			mouseFlag |= MouseFlags.Button1Pressed;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.Button1Released) != 0) {
-			mouseFlag |= MouseFlags.Button1Released;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.Button1Clicked) != 0) {
-			mouseFlag |= MouseFlags.Button1Clicked;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.Button1DoubleClicked) != 0) {
-			mouseFlag |= MouseFlags.Button1DoubleClicked;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.Button1TripleClicked) != 0) {
-			mouseFlag |= MouseFlags.Button1TripleClicked;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.Button2Pressed) != 0) {
-			mouseFlag |= MouseFlags.Button2Pressed;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.Button2Released) != 0) {
-			mouseFlag |= MouseFlags.Button2Released;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.Button2Clicked) != 0) {
-			mouseFlag |= MouseFlags.Button2Clicked;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.Button2DoubleClicked) != 0) {
-			mouseFlag |= MouseFlags.Button2DoubleClicked;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.Button2TripleClicked) != 0) {
-			mouseFlag |= MouseFlags.Button2TripleClicked;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.Button3Pressed) != 0) {
-			mouseFlag |= MouseFlags.Button3Pressed;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.Button3Released) != 0) {
-			mouseFlag |= MouseFlags.Button3Released;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.Button3Clicked) != 0) {
-			mouseFlag |= MouseFlags.Button3Clicked;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.Button3DoubleClicked) != 0) {
-			mouseFlag |= MouseFlags.Button3DoubleClicked;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.Button3TripleClicked) != 0) {
-			mouseFlag |= MouseFlags.Button3TripleClicked;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledUp) != 0) {
-			mouseFlag |= MouseFlags.WheeledUp;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledDown) != 0) {
-			mouseFlag |= MouseFlags.WheeledDown;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledLeft) != 0) {
-			mouseFlag |= MouseFlags.WheeledLeft;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledRight) != 0) {
-			mouseFlag |= MouseFlags.WheeledRight;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.Button4Pressed) != 0) {
-			mouseFlag |= MouseFlags.Button4Pressed;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.Button4Released) != 0) {
-			mouseFlag |= MouseFlags.Button4Released;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.Button4Clicked) != 0) {
-			mouseFlag |= MouseFlags.Button4Clicked;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.Button4DoubleClicked) != 0) {
-			mouseFlag |= MouseFlags.Button4DoubleClicked;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.Button4TripleClicked) != 0) {
-			mouseFlag |= MouseFlags.Button4TripleClicked;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.ReportMousePosition) != 0) {
-			mouseFlag |= MouseFlags.ReportMousePosition;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.ButtonShift) != 0) {
-			mouseFlag |= MouseFlags.ButtonShift;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.ButtonCtrl) != 0) {
-			mouseFlag |= MouseFlags.ButtonCtrl;
-		}
-		if ((me.ButtonState & NetEvents.MouseButtonState.ButtonAlt) != 0) {
-			mouseFlag |= MouseFlags.ButtonAlt;
-		}
-
-		return new MouseEvent () {
-			X = me.Position.X,
-			Y = me.Position.Y,
-			Flags = mouseFlag
-		};
-	}
-
 	public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control)
 	public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control)
 	{
 	{
-		NetEvents.InputResult input = new NetEvents.InputResult {
+		var input = new InputResult {
 			EventType = NetEvents.EventType.Key,
 			EventType = NetEvents.EventType.Key,
 			ConsoleKeyInfo = new ConsoleKeyInfo (keyChar, key, shift, alt, control)
 			ConsoleKeyInfo = new ConsoleKeyInfo (keyChar, key, shift, alt, control)
 		};
 		};
@@ -1280,12 +1266,8 @@ internal class NetDriver : ConsoleDriver {
 
 
 
 
 	#region Not Implemented
 	#region Not Implemented
-	public override void Suspend ()
-	{
-		throw new NotImplementedException ();
-	}
+	public override void Suspend () => throw new NotImplementedException ();
 	#endregion
 	#endregion
-
 }
 }
 
 
 /// <summary>
 /// <summary>
@@ -1296,19 +1278,19 @@ internal class NetDriver : ConsoleDriver {
 /// <remarks>
 /// <remarks>
 /// This implementation is used for NetDriver.
 /// This implementation is used for NetDriver.
 /// </remarks>
 /// </remarks>
-internal class NetMainLoop : IMainLoopDriver {
-	readonly ManualResetEventSlim _eventReady = new ManualResetEventSlim (false);
-	readonly ManualResetEventSlim _waitForProbe = new ManualResetEventSlim (false);
-	readonly Queue<NetEvents.InputResult?> _resultQueue = new Queue<NetEvents.InputResult?> ();
+class NetMainLoop : IMainLoopDriver {
+	readonly ManualResetEventSlim _eventReady = new (false);
+	readonly ManualResetEventSlim _waitForProbe = new (false);
+	readonly Queue<InputResult?> _resultQueue = new ();
 	MainLoop _mainLoop;
 	MainLoop _mainLoop;
-	CancellationTokenSource _eventReadyTokenSource = new CancellationTokenSource ();
-	readonly CancellationTokenSource _inputHandlerTokenSource = new CancellationTokenSource ();
+	CancellationTokenSource _eventReadyTokenSource = new ();
+	readonly CancellationTokenSource _inputHandlerTokenSource = new ();
 	internal NetEvents _netEvents;
 	internal NetEvents _netEvents;
 
 
 	/// <summary>
 	/// <summary>
 	/// Invoked when a Key is pressed.
 	/// Invoked when a Key is pressed.
 	/// </summary>
 	/// </summary>
-	internal Action<NetEvents.InputResult> ProcessInput;
+	internal Action<InputResult> ProcessInput;
 
 
 	/// <summary>
 	/// <summary>
 	/// Initializes the class with the console driver.
 	/// Initializes the class with the console driver.
@@ -1367,16 +1349,13 @@ internal class NetMainLoop : IMainLoopDriver {
 		Task.Run (NetInputHandler, _inputHandlerTokenSource.Token);
 		Task.Run (NetInputHandler, _inputHandlerTokenSource.Token);
 	}
 	}
 
 
-	void IMainLoopDriver.Wakeup ()
-	{
-		_eventReady.Set ();
-	}
+	void IMainLoopDriver.Wakeup () => _eventReady.Set ();
 
 
 	bool IMainLoopDriver.EventsPending ()
 	bool IMainLoopDriver.EventsPending ()
 	{
 	{
 		_waitForProbe.Set ();
 		_waitForProbe.Set ();
 
 
-		if (_mainLoop.CheckTimersAndIdleHandlers (out var waitTimeout)) {
+		if (_mainLoop.CheckTimersAndIdleHandlers (out int waitTimeout)) {
 			return true;
 			return true;
 		}
 		}
 
 

+ 157 - 167
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -24,6 +24,7 @@ using System.Threading.Tasks;
 using System.Diagnostics;
 using System.Diagnostics;
 using Terminal.Gui.ConsoleDrivers;
 using Terminal.Gui.ConsoleDrivers;
 using static Unix.Terminal.Delegates;
 using static Unix.Terminal.Delegates;
+using static Terminal.Gui.ConsoleDrivers.ConsoleKeyMapping;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
@@ -371,7 +372,7 @@ internal class WindowsConsole {
 		[FieldOffset (4), MarshalAs (UnmanagedType.U2)]
 		[FieldOffset (4), MarshalAs (UnmanagedType.U2)]
 		public ushort wRepeatCount;
 		public ushort wRepeatCount;
 		[FieldOffset (6), MarshalAs (UnmanagedType.U2)]
 		[FieldOffset (6), MarshalAs (UnmanagedType.U2)]
-		public ushort wVirtualKeyCode;
+		public VK wVirtualKeyCode;
 		[FieldOffset (8), MarshalAs (UnmanagedType.U2)]
 		[FieldOffset (8), MarshalAs (UnmanagedType.U2)]
 		public ushort wVirtualScanCode;
 		public ushort wVirtualScanCode;
 		[FieldOffset (10)]
 		[FieldOffset (10)]
@@ -901,66 +902,31 @@ internal class WindowsDriver : ConsoleDriver {
 	}
 	}
 #endif
 #endif
 
 
+
 	KeyCode MapKey (WindowsConsole.ConsoleKeyInfoEx keyInfoEx)
 	KeyCode MapKey (WindowsConsole.ConsoleKeyInfoEx keyInfoEx)
 	{
 	{
 		var keyInfo = keyInfoEx.ConsoleKeyInfo;
 		var keyInfo = keyInfoEx.ConsoleKeyInfo;
 		switch (keyInfo.Key) {
 		switch (keyInfo.Key) {
-		case ConsoleKey.Escape:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.Esc);
-		case ConsoleKey.Tab:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.Tab);
-		case ConsoleKey.Clear:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.Clear);
-		case ConsoleKey.Home:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.Home);
-		case ConsoleKey.End:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.End);
-		case ConsoleKey.LeftArrow:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.CursorLeft);
-		case ConsoleKey.RightArrow:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.CursorRight);
-		case ConsoleKey.UpArrow:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.CursorUp);
-		case ConsoleKey.DownArrow:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.CursorDown);
-		case ConsoleKey.PageUp:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.PageUp);
-		case ConsoleKey.PageDown:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.PageDown);
-		case ConsoleKey.Enter:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.Enter);
-		case ConsoleKey.Spacebar:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? KeyCode.Space : (KeyCode)keyInfo.KeyChar);
-		case ConsoleKey.Backspace:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.Backspace);
-		case ConsoleKey.Delete:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.DeleteChar);
-		case ConsoleKey.Insert:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.InsertChar);
-		case ConsoleKey.PrintScreen:
-			return ConsoleKeyMapping.MapKeyModifiers (keyInfo, KeyCode.PrintScreen);
-
-		//case ConsoleKey.NumPad0:
-		//	return keyInfoEx.NumLock ? Key.D0 : Key.InsertChar;
-		//case ConsoleKey.NumPad1:
-		//	return keyInfoEx.NumLock ? Key.D1 : Key.End;
-		//case ConsoleKey.NumPad2:
-		//	return keyInfoEx.NumLock ? Key.D2 : Key.CursorDown;
-		//case ConsoleKey.NumPad3:
-		//	return keyInfoEx.NumLock ? Key.D3 : Key.PageDown;
-		//case ConsoleKey.NumPad4:
-		//	return keyInfoEx.NumLock ? Key.D4 : Key.CursorLeft;
-		//case ConsoleKey.NumPad5:
-		//	return keyInfoEx.NumLock ? Key.D5 : (Key)((uint)keyInfo.KeyChar);
-		//case ConsoleKey.NumPad6:
-		//	return keyInfoEx.NumLock ? Key.D6 : Key.CursorRight;
-		//case ConsoleKey.NumPad7:
-		//	return keyInfoEx.NumLock ? Key.D7 : Key.Home;
-		//case ConsoleKey.NumPad8:
-		//	return keyInfoEx.NumLock ? Key.D8 : Key.CursorUp;
-		//case ConsoleKey.NumPad9:
-		//	return keyInfoEx.NumLock ? Key.D9 : Key.PageUp;
-
+		case ConsoleKey.D0:
+		case ConsoleKey.D1:
+		case ConsoleKey.D2:
+		case ConsoleKey.D3:
+		case ConsoleKey.D4:
+		case ConsoleKey.D5:
+		case ConsoleKey.D6:
+		case ConsoleKey.D7:
+		case ConsoleKey.D8:
+		case ConsoleKey.D9:
+		case ConsoleKey.NumPad0:
+		case ConsoleKey.NumPad1:
+		case ConsoleKey.NumPad2:
+		case ConsoleKey.NumPad3:
+		case ConsoleKey.NumPad4:
+		case ConsoleKey.NumPad5:
+		case ConsoleKey.NumPad6:
+		case ConsoleKey.NumPad7:
+		case ConsoleKey.NumPad8:
+		case ConsoleKey.NumPad9:
 		case ConsoleKey.Oem1:
 		case ConsoleKey.Oem1:
 		case ConsoleKey.Oem2:
 		case ConsoleKey.Oem2:
 		case ConsoleKey.Oem3:
 		case ConsoleKey.Oem3:
@@ -970,148 +936,168 @@ internal class WindowsDriver : ConsoleDriver {
 		case ConsoleKey.Oem7:
 		case ConsoleKey.Oem7:
 		case ConsoleKey.Oem8:
 		case ConsoleKey.Oem8:
 		case ConsoleKey.Oem102:
 		case ConsoleKey.Oem102:
-			var ret = ConsoleKeyMapping.MapKeyModifiers (keyInfo, (KeyCode)((uint)keyInfo.KeyChar));
-			if (ret.HasFlag (KeyCode.ShiftMask)) {
-				ret &= ~KeyCode.ShiftMask;
-			}
-			return ret;
-
+		case ConsoleKey.Multiply:
+		case ConsoleKey.Add:
+		case ConsoleKey.Separator:
+		case ConsoleKey.Subtract:
+		case ConsoleKey.Decimal:
+		case ConsoleKey.Divide:
 		case ConsoleKey.OemPeriod:
 		case ConsoleKey.OemPeriod:
 		case ConsoleKey.OemComma:
 		case ConsoleKey.OemComma:
 		case ConsoleKey.OemPlus:
 		case ConsoleKey.OemPlus:
 		case ConsoleKey.OemMinus:
 		case ConsoleKey.OemMinus:
-			return (KeyCode)((uint)keyInfo.KeyChar);
-		}
-
-		var key = keyInfo.Key;
-
-		if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
-			var delta = key - ConsoleKey.A;
-			if (keyInfo.Modifiers == ConsoleModifiers.Control) {
-				return (KeyCode)(((uint)KeyCode.CtrlMask) | ((uint)KeyCode.A + delta));
-			}
-			if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
-				return (KeyCode)(((uint)KeyCode.AltMask) | ((uint)KeyCode.A + delta));
-			}
-			if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) {
-				return ConsoleKeyMapping.MapKeyModifiers (keyInfo, (KeyCode)((uint)KeyCode.A + delta));
-			}
-			if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
-				if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) {
-					return ConsoleKeyMapping.MapKeyModifiers (keyInfo, (KeyCode)((uint)KeyCode.A + delta));
-				}
+			// These virtual key codes are mapped differently depending on the keyboard layout in use.
+			// We use the Win32 API to map them to the correct character.
+			var mapResult = MapVKtoChar ((VK)keyInfo.Key);
+			if (mapResult == 0) {
+				// There is no mapping - this should not happen
+				Debug.Assert (mapResult != 0, $@"Unable to map the virtual key code {keyInfo.Key}.");
+				return KeyCode.Null;
 			}
 			}
 
 
-			if (((keyInfo.Modifiers == ConsoleModifiers.Shift) ^ (keyInfoEx.CapsLock))) {
-				if (keyInfo.KeyChar <= (uint)KeyCode.Z) {
-					return (KeyCode)((uint)KeyCode.A + delta) | KeyCode.ShiftMask;
+			// An un-shifted character value is in the low order word of the return value.
+			var mappedChar = (char)(mapResult & 0x0000FFFF);
+
+			if (keyInfo.KeyChar == 0) {
+				// 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. 
+				if ((mapResult & 0x80000000) != 0) {
+					// Dead key (e.g. Oem2 '~'/'^' on POR keyboard)
+					// 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').
+					//    - If user presses dead key again, the right thing happens (app will see `~~`)
+					//    - This is what Notepad etc... appear to do
+					// Option 2: Expand the API to indicate the KeyCode is a dead key
+					//    - Enables apps to do their own dead key processing
+					//    - Adds complexity; no dev has asked for this (yet).
+					// We choose Option 1 for now.
+					return KeyCode.Null;
+
+					// Note: Ctrl-Deadkey (like Oem3 '`'/'~` on ENG) can't be supported.
+					// Sadly, the charVal is just the deadkey and subsequent key events do not contain
+					// any info that the previous event was a deadkey.
+					// Note WT does not support Ctrl-Deadkey either.
 				}
 				}
-			}
 
 
-			if (((KeyCode)((uint)keyInfo.KeyChar) & KeyCode.Space) == 0) {
-				return (KeyCode)((uint)keyInfo.KeyChar) & ~KeyCode.Space;
-			}
+				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 
+					// returned (e.g. on ENG OemPlus un-shifted is =, not +). This is important
+					// for key persistence ("Ctrl++" vs. "Ctrl+=").
+					mappedChar = keyInfo.Key switch {
+						ConsoleKey.OemPeriod => '.',
+						ConsoleKey.OemComma => ',',
+						ConsoleKey.OemPlus => '+',
+						ConsoleKey.OemMinus => '-',
+						_ => mappedChar
+					};
+				}
 
 
-			if (((KeyCode)((uint)keyInfo.KeyChar) & KeyCode.Space) != 0) {
-				if (((KeyCode)((uint)keyInfo.KeyChar) & ~KeyCode.Space) == (KeyCode)keyInfo.Key) {
-					return (KeyCode)((uint)keyInfo.KeyChar) & ~KeyCode.Space;
+				// Return the mappedChar with they modifiers. Because mappedChar is un-shifted, if Shift was down
+				// we should keep it
+				return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)mappedChar);
+			} else {
+				// KeyChar is printable
+				if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt) && keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control)) {
+					// AltGr support - AltGr is equivalent to Ctrl+Alt - the correct char is in KeyChar
+					return (KeyCode)keyInfo.KeyChar;
 				}
 				}
-				return (KeyCode)((uint)keyInfo.KeyChar);
-			}
 
 
-			return (KeyCode)(uint)keyInfo.KeyChar;
+				if (keyInfo.Modifiers != ConsoleModifiers.Shift) {
+					// If Shift wasn't down we don't need to do anything but return the mappedChar
+					return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)(mappedChar));
+				}
 
 
+				// Strip off Shift - We got here because they KeyChar from Windows is the shifted char (e.g. "Ç")
+				// and passing on Shift would be redundant.
+				return MapToKeyCodeModifiers (keyInfo.Modifiers & ~ConsoleModifiers.Shift, (KeyCode)keyInfo.KeyChar);
+			}
 		}
 		}
 
 
-		if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
-			var delta = key - ConsoleKey.D0;
-			if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
-				return (KeyCode)(((uint)KeyCode.AltMask) | ((uint)KeyCode.D0 + delta));
-			}
-			if (keyInfo.Modifiers == ConsoleModifiers.Control) {
-				return (KeyCode)(((uint)KeyCode.CtrlMask) | ((uint)KeyCode.D0 + delta));
+		// A..Z are special cased:
+		// - Alone, they represent lowercase a...z
+		// - With ShiftMask they are A..Z
+		// - If CapsLock is on the above is reversed.
+		// - If Alt and/or Ctrl are present, treat as upper case
+		if (keyInfo.Key is >= ConsoleKey.A and <= ConsoleKey.Z) {
+			if (keyInfo.KeyChar == 0) {
+				// KeyChar is not printable - possibly an AltGr key?
+				// AltGr support - AltGr is equivalent to Ctrl+Alt
+				if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt) && keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control)) {
+					return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)(uint)keyInfo.Key);
+				}
 			}
 			}
-			if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) {
-				return ConsoleKeyMapping.MapKeyModifiers (keyInfo, (KeyCode)((uint)KeyCode.D0 + delta));
+
+			if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt) || keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control)) {
+				return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)(uint)keyInfo.Key);
 			}
 			}
-			if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
-				if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)KeyCode.D0 + delta)) {
-					return ConsoleKeyMapping.MapKeyModifiers (keyInfo, (KeyCode)((uint)KeyCode.D0 + delta));
+
+			if (((keyInfo.Modifiers == ConsoleModifiers.Shift) ^ (keyInfoEx.CapsLock))) {
+				// If (ShiftMask is on and CapsLock is off) or (ShiftMask is off and CapsLock is on) add the ShiftMask
+				if (char.IsUpper (keyInfo.KeyChar)) {
+					return (KeyCode)((uint)keyInfo.Key) | KeyCode.ShiftMask;
 				}
 				}
 			}
 			}
-			return (KeyCode)((uint)keyInfo.KeyChar);
+			return (KeyCode)(uint)keyInfo.KeyChar;
 		}
 		}
 
 
-		if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) {
-			var delta = key - ConsoleKey.F1;
-			if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
-				return ConsoleKeyMapping.MapKeyModifiers (keyInfo, (KeyCode)((uint)KeyCode.F1 + delta));
+		// Handle control keys whose VK codes match the related ASCII value (those below ASCII 33) like ESC
+		if (Enum.IsDefined (typeof (KeyCode), (uint)keyInfo.Key)) {
+			// If the key is JUST a modifier, return it as just that key
+			if (keyInfo.Key == (ConsoleKey)VK.SHIFT) { // Shift 16
+				return KeyCode.ShiftMask;
 			}
 			}
 
 
-			return (KeyCode)((uint)KeyCode.F1 + delta);
-		}
+			if (keyInfo.Key == (ConsoleKey)VK.CONTROL) { // Ctrl 17
+				return KeyCode.CtrlMask;
+			}
 
 
-		// If the key is JUST a modifier, return it as that key
-		if (key == (ConsoleKey)16) { // Shift
-			return KeyCode.ShiftKey;
-		}
+			if (keyInfo.Key == (ConsoleKey)VK.MENU) { // Alt 18
+				return KeyCode.AltMask;
+			}
 
 
-		if (key == (ConsoleKey)17) { // Ctrl
-			return KeyCode.CtrlKey;
+			if (keyInfo.KeyChar == 0) {
+				return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)(keyInfo.KeyChar));
+			} else {
+				return MapToKeyCodeModifiers (keyInfo.Modifiers & ~ConsoleModifiers.Shift, (KeyCode)(keyInfo.KeyChar));
+			}
 		}
 		}
 
 
-		if (key == (ConsoleKey)18) { // Alt
-			return KeyCode.AltKey;
+		// Handle control keys (e.g. CursorUp)
+		if (Enum.IsDefined (typeof (KeyCode), ((uint)keyInfo.Key + (uint)KeyCode.MaxCodePoint))) {
+			return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)((uint)keyInfo.Key + (uint)KeyCode.MaxCodePoint));
 		}
 		}
 
 
-		return ConsoleKeyMapping.MapKeyModifiers (keyInfo, (KeyCode)((uint)keyInfo.KeyChar));
+		return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)(keyInfo.KeyChar));
 	}
 	}
 
 
-	bool _altDown = false;
-
 	internal void ProcessInput (WindowsConsole.InputRecord inputEvent)
 	internal void ProcessInput (WindowsConsole.InputRecord inputEvent)
 	{
 	{
 		switch (inputEvent.EventType) {
 		switch (inputEvent.EventType) {
 		case WindowsConsole.EventType.Key:
 		case WindowsConsole.EventType.Key:
-			var fromPacketKey = inputEvent.KeyEvent.wVirtualKeyCode == (uint)ConsoleKey.Packet;
-			if (fromPacketKey) {
+			if (inputEvent.KeyEvent.wVirtualKeyCode == (VK)ConsoleKey.Packet) {
+				// Used to pass Unicode characters as if they were keystrokes.
+				// The VK_PACKET key is the low word of a 32-bit
+				// Virtual Key value used for non-keyboard input methods.
 				inputEvent.KeyEvent = FromVKPacketToKeyEventRecord (inputEvent.KeyEvent);
 				inputEvent.KeyEvent = FromVKPacketToKeyEventRecord (inputEvent.KeyEvent);
 			}
 			}
 			var keyInfo = ToConsoleKeyInfoEx (inputEvent.KeyEvent);
 			var keyInfo = ToConsoleKeyInfoEx (inputEvent.KeyEvent);
-			//Debug.WriteLine ($"event: {inputEvent.ToString ()} {keyInfo.ToString (keyInfo)}");
-
+			//Debug.WriteLine ($"event: KBD: {GetKeyboardLayoutName()} {inputEvent.ToString ()} {keyInfo.ToString (keyInfo)}");
 
 
 			var map = MapKey (keyInfo);
 			var map = MapKey (keyInfo);
 
 
+			if (map == KeyCode.Null) {
+				break;
+			}
+
 			if (inputEvent.KeyEvent.bKeyDown) {
 			if (inputEvent.KeyEvent.bKeyDown) {
-				_altDown = keyInfo.ConsoleKeyInfo.Modifiers == ConsoleModifiers.Alt;
-				// Avoid sending repeat keydowns
+				// Avoid sending repeat key down events
 				OnKeyDown (new Key (map));
 				OnKeyDown (new Key (map));
 			} else {
 			} else {
-				var keyPressedEventArgs = new Key (map);
-
-				// PROTOTYPE: This logic enables `Alt` key presses (down, up, pressed).
-				// However, if while the 'Alt' key is down, if another key is pressed and
-				// released, there will be a keypressed event for that and the
-				// keypressed event for just `Alt` will be suppressed. 
-				// This allows MenuBar to have `Alt` as a keybinding
-				if (map != KeyCode.AltMask) {
-					if (keyInfo.ConsoleKeyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt)) {
-						if (_altDown) {
-							_altDown = false;
-							OnKeyUp (new Key (map));
-						}
-
-					}
-					_altDown = false;
-					// KeyUp of an Alt-key press. 
-					OnKeyUp (keyPressedEventArgs);
-				} else {
-					OnKeyUp (keyPressedEventArgs);
-					if (_altDown) {
-						_altDown = false;
-					}
-				}
+				OnKeyUp (new Key (map));
 			}
 			}
 
 
 			break;
 			break;
@@ -1422,7 +1408,7 @@ internal class WindowsDriver : ConsoleDriver {
 
 
 	public WindowsConsole.KeyEventRecord FromVKPacketToKeyEventRecord (WindowsConsole.KeyEventRecord keyEvent)
 	public WindowsConsole.KeyEventRecord FromVKPacketToKeyEventRecord (WindowsConsole.KeyEventRecord keyEvent)
 	{
 	{
-		if (keyEvent.wVirtualKeyCode != (uint)ConsoleKey.Packet) {
+		if (keyEvent.wVirtualKeyCode != (VK)ConsoleKey.Packet) {
 			return keyEvent;
 			return keyEvent;
 		}
 		}
 
 
@@ -1438,14 +1424,17 @@ internal class WindowsDriver : ConsoleDriver {
 		    keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightControlPressed)) {
 		    keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightControlPressed)) {
 			mod |= ConsoleModifiers.Control;
 			mod |= ConsoleModifiers.Control;
 		}
 		}
-		var cKeyInfo = ConsoleKeyMapping.GetConsoleKeyFromKey (keyEvent.UnicodeChar, mod, out uint scanCode);
+		var cKeyInfo = new ConsoleKeyInfo (keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode,
+			mod.HasFlag (ConsoleModifiers.Shift), mod.HasFlag (ConsoleModifiers.Alt), mod.HasFlag (ConsoleModifiers.Control));
+		cKeyInfo = DecodeVKPacketToKConsoleKeyInfo (cKeyInfo);
+		var scanCode = GetScanCodeFromConsoleKeyInfo (cKeyInfo);
 
 
 		return new WindowsConsole.KeyEventRecord {
 		return new WindowsConsole.KeyEventRecord {
 			UnicodeChar = cKeyInfo.KeyChar,
 			UnicodeChar = cKeyInfo.KeyChar,
 			bKeyDown = keyEvent.bKeyDown,
 			bKeyDown = keyEvent.bKeyDown,
 			dwControlKeyState = keyEvent.dwControlKeyState,
 			dwControlKeyState = keyEvent.dwControlKeyState,
 			wRepeatCount = keyEvent.wRepeatCount,
 			wRepeatCount = keyEvent.wRepeatCount,
-			wVirtualKeyCode = (ushort)cKeyInfo.Key,
+			wVirtualKeyCode = (VK)cKeyInfo.Key,
 			wVirtualScanCode = (ushort)scanCode
 			wVirtualScanCode = (ushort)scanCode
 		};
 		};
 	}
 	}
@@ -1591,19 +1580,19 @@ internal class WindowsDriver : ConsoleDriver {
 		if (shift) {
 		if (shift) {
 			controlKey |= WindowsConsole.ControlKeyState.ShiftPressed;
 			controlKey |= WindowsConsole.ControlKeyState.ShiftPressed;
 			keyEvent.UnicodeChar = '\0';
 			keyEvent.UnicodeChar = '\0';
-			keyEvent.wVirtualKeyCode = 16;
+			keyEvent.wVirtualKeyCode = VK.SHIFT;
 		}
 		}
 		if (alt) {
 		if (alt) {
 			controlKey |= WindowsConsole.ControlKeyState.LeftAltPressed;
 			controlKey |= WindowsConsole.ControlKeyState.LeftAltPressed;
 			controlKey |= WindowsConsole.ControlKeyState.RightAltPressed;
 			controlKey |= WindowsConsole.ControlKeyState.RightAltPressed;
 			keyEvent.UnicodeChar = '\0';
 			keyEvent.UnicodeChar = '\0';
-			keyEvent.wVirtualKeyCode = 18;
+			keyEvent.wVirtualKeyCode = VK.MENU;
 		}
 		}
 		if (control) {
 		if (control) {
 			controlKey |= WindowsConsole.ControlKeyState.LeftControlPressed;
 			controlKey |= WindowsConsole.ControlKeyState.LeftControlPressed;
 			controlKey |= WindowsConsole.ControlKeyState.RightControlPressed;
 			controlKey |= WindowsConsole.ControlKeyState.RightControlPressed;
 			keyEvent.UnicodeChar = '\0';
 			keyEvent.UnicodeChar = '\0';
-			keyEvent.wVirtualKeyCode = 17;
+			keyEvent.wVirtualKeyCode = VK.CONTROL;
 		}
 		}
 		keyEvent.dwControlKeyState = controlKey;
 		keyEvent.dwControlKeyState = controlKey;
 
 
@@ -1614,11 +1603,12 @@ internal class WindowsDriver : ConsoleDriver {
 		}
 		}
 
 
 		keyEvent.UnicodeChar = keyChar;
 		keyEvent.UnicodeChar = keyChar;
-		if ((uint)key < 255) {
-			keyEvent.wVirtualKeyCode = (ushort)key;
-		} else {
-			keyEvent.wVirtualKeyCode = '\0';
-		}
+		//if ((uint)key < 255) {
+		//	keyEvent.wVirtualKeyCode = (ushort)key;
+		//} else {
+		//	keyEvent.wVirtualKeyCode = '\0';
+		//}
+		keyEvent.wVirtualKeyCode = (VK)key;
 
 
 		input.KeyEvent = keyEvent;
 		input.KeyEvent = keyEvent;
 
 

+ 64 - 60
Terminal.Gui/Input/Key.cs

@@ -132,7 +132,8 @@ public class Key : EventArgs, IEquatable<Key> {
 	/// The encoded key value. 
 	/// The encoded key value. 
 	/// </summary>
 	/// </summary>
 	/// <para>
 	/// <para>
-	/// IMPORTANT: Lowercase alpha keys are encoded (in <see cref="Gui.KeyCode"/>) as values between 65 and 90 corresponding to the un-shifted A to Z keys on a keyboard. Enum values
+	/// IMPORTANT: Lowercase alpha keys are encoded (in <see cref="Gui.KeyCode"/>) as values between 65 and
+	/// 90 corresponding to the un-shifted A to Z keys on a keyboard. Enum values
 	/// are provided for these (e.g. <see cref="KeyCode.A"/>, <see cref="KeyCode.B"/>, etc.). Even though the values are the same as the ASCII
 	/// are provided for these (e.g. <see cref="KeyCode.A"/>, <see cref="KeyCode.B"/>, etc.). Even though the values are the same as the ASCII
 	/// values for uppercase characters, these enum values represent *lowercase*, un-shifted characters.
 	/// values for uppercase characters, these enum values represent *lowercase*, un-shifted characters.
 	/// </para>
 	/// </para>
@@ -147,24 +148,36 @@ public class Key : EventArgs, IEquatable<Key> {
 	public KeyBindingScope Scope { get; set; } = KeyBindingScope.Focused;
 	public KeyBindingScope Scope { get; set; } = KeyBindingScope.Focused;
 
 
 	/// <summary>
 	/// <summary>
-	/// The key value as a Rune. This is the actual value of the key pressed, and is independent of the modifiers.
+	/// The key value as a Rune. This is the actual value of the key pressed, and is independent of the modifiers. Useful
+	/// for determining if a key represents is a printable character.
 	/// </summary>
 	/// </summary>
 	/// <remarks>
 	/// <remarks>
 	/// <para>
 	/// <para>
-	/// If the key is a letter (a-z or A-Z), the Rune be the upper or lower case letter depending on whether
-	/// the shift key was pressed.
+	/// Keys with Ctrl or Alt modifiers will return <see langword="default"/>. 
+	/// </para>
+	/// <para>
+	/// If the key is a letter key (A-Z), the Rune will be the upper or lower case letter depending on whether
+	/// <see cref="KeyCode.ShiftMask"/> is set.
+	/// </para>
+	/// <para>
 	/// If the key is outside of the <see cref="KeyCode.CharMask"/> range, the returned Rune will be <see langword="default"/>.
 	/// If the key is outside of the <see cref="KeyCode.CharMask"/> range, the returned Rune will be <see langword="default"/>.
 	/// </para>
 	/// </para>
 	/// </remarks>
 	/// </remarks>
 	public Rune AsRune => ToRune (KeyCode);
 	public Rune AsRune => ToRune (KeyCode);
 
 
 	/// <summary>
 	/// <summary>
-	/// Converts a <see cref="KeyCode"/> to a <see cref="Rune"/>.
+	/// Converts a <see cref="KeyCode"/> to a <see cref="Rune"/>. Useful
+	/// for determining if a key represents is a printable character.
 	/// </summary>
 	/// </summary>
 	/// <remarks>
 	/// <remarks>
 	/// <para>
 	/// <para>
-	/// If the key is a letter (a-z or A-Z), the Rune be the upper or lower case letter depending on whether
-	/// the shift key was pressed.
+	/// Keys with Ctrl or Alt modifiers will return <see langword="default"/>. 
+	/// </para>
+	/// <para>
+	/// If the key is a letter key (A-Z), the Rune will be the upper or lower case letter depending on whether
+	/// <see cref="KeyCode.ShiftMask"/> is set.
+	/// </para>
+	/// <para>
 	/// If the key is outside of the <see cref="KeyCode.CharMask"/> range, the returned Rune will be <see langword="default"/>.
 	/// If the key is outside of the <see cref="KeyCode.CharMask"/> range, the returned Rune will be <see langword="default"/>.
 	/// </para>
 	/// </para>
 	/// </remarks>
 	/// </remarks>
@@ -176,23 +189,26 @@ public class Key : EventArgs, IEquatable<Key> {
 			return default;
 			return default;
 		}
 		}
 
 
-		// Extract the base key (removing modifier flags)
-		var baseKey = key & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask;
+		// Extract the base key code
+		var baseKey = key;
+		if (baseKey.HasFlag(KeyCode.ShiftMask)) {
+			baseKey &= ~KeyCode.ShiftMask;
+		}
 
 
 		switch (baseKey) {
 		switch (baseKey) {
 		case >= KeyCode.A and <= KeyCode.Z when !key.HasFlag (KeyCode.ShiftMask):
 		case >= KeyCode.A and <= KeyCode.Z when !key.HasFlag (KeyCode.ShiftMask):
-			return new Rune ((char)(baseKey + 32));
-		case >= KeyCode.A and <= KeyCode.Z:
-			return new Rune ((char)baseKey);
+			return new Rune ((uint)(baseKey + 32));
+		case >= KeyCode.A and <= KeyCode.Z when key.HasFlag (KeyCode.ShiftMask):
+			return new Rune ((uint)baseKey);
 		case > KeyCode.Null and < KeyCode.A:
 		case > KeyCode.Null and < KeyCode.A:
-			return new Rune ((char)baseKey);
+			return new Rune ((uint)baseKey);
 		}
 		}
 
 
 		if (Enum.IsDefined (typeof (KeyCode), baseKey)) {
 		if (Enum.IsDefined (typeof (KeyCode), baseKey)) {
 			return default;
 			return default;
 		}
 		}
 
 
-		return new Rune ((char)baseKey);
+		return new Rune ((uint)baseKey);
 	}
 	}
 
 
 	/// <summary>
 	/// <summary>
@@ -214,7 +230,9 @@ public class Key : EventArgs, IEquatable<Key> {
 	public bool IsCtrl => (KeyCode & KeyCode.CtrlMask) != 0;
 	public bool IsCtrl => (KeyCode & KeyCode.CtrlMask) != 0;
 
 
 	/// <summary>
 	/// <summary>
-	/// Gets a value indicating whether the KeyCode is composed of a lower case letter from 'a' to 'z', independent of the shift key.
+	/// Gets a value indicating whether the key represents a key in the range of <see cref="KeyCode.A"/> to <see cref="KeyCode.Z"/>,
+	/// regardless of the <see cref="KeyCode.ShiftMask"/>. This is useful for testing if a key is based on these keys which are
+	/// special cased.
 	/// </summary>
 	/// </summary>
 	/// <remarks>
 	/// <remarks>
 	/// IMPORTANT: Lowercase alpha keys are encoded in <see cref="Key.KeyCode"/> as values between 65 and 90 corresponding to
 	/// IMPORTANT: Lowercase alpha keys are encoded in <see cref="Key.KeyCode"/> as values between 65 and 90 corresponding to
@@ -224,7 +242,9 @@ public class Key : EventArgs, IEquatable<Key> {
 	public bool IsKeyCodeAtoZ => GetIsKeyCodeAtoZ (KeyCode);
 	public bool IsKeyCodeAtoZ => GetIsKeyCodeAtoZ (KeyCode);
 
 
 	/// <summary>
 	/// <summary>
-	/// Tests if a KeyCode is composed of a lower case letter from 'a' to 'z', independent of the shift key.
+	/// Tests if a KeyCode represents a key in the range of <see cref="KeyCode.A"/> to <see cref="KeyCode.Z"/>,
+	/// regardless of the <see cref="KeyCode.ShiftMask"/>. This is useful for testing if a key is based on these keys which are
+	/// special cased.
 	/// </summary>
 	/// </summary>
 	/// <remarks>
 	/// <remarks>
 	/// IMPORTANT: Lowercase alpha keys are encoded in <see cref="Key.KeyCode"/> as values between 65 and 90 corresponding to
 	/// IMPORTANT: Lowercase alpha keys are encoded in <see cref="Key.KeyCode"/> as values between 65 and 90 corresponding to
@@ -303,7 +323,8 @@ public class Key : EventArgs, IEquatable<Key> {
 
 
 	#region Operators
 	#region Operators
 	/// <summary>
 	/// <summary>
-	/// Explicitly cast a <see cref="Key"/> to a <see cref="Rune"/>. The conversion is lossy. 
+	/// Explicitly cast a <see cref="Key"/> to a <see cref="Rune"/>. The conversion is lossy because properties
+	/// such as <see cref="Handled"/> are not encoded in <see cref="KeyCode"/>.
 	/// </summary>
 	/// </summary>
 	/// <remarks>
 	/// <remarks>
 	/// Uses <see cref="AsRune"/>.
 	/// Uses <see cref="AsRune"/>.
@@ -311,14 +332,17 @@ public class Key : EventArgs, IEquatable<Key> {
 	/// <param name="kea"></param>
 	/// <param name="kea"></param>
 	public static explicit operator Rune (Key kea) => kea.AsRune;
 	public static explicit operator Rune (Key kea) => kea.AsRune;
 
 
+	// BUGBUG: (Tig) I do not think this cast operator is really needed. 
 	/// <summary>
 	/// <summary>
-	/// Explicitly cast <see cref="Key"/> to a <see langword="char"/>. The conversion is lossy. 
+	/// Explicitly cast <see cref="Key"/> to a <see langword="uint"/>. The conversion is lossy because properties
+	/// such as <see cref="Handled"/> are not encoded in <see cref="KeyCode"/>.
 	/// </summary>
 	/// </summary>
 	/// <param name="kea"></param>
 	/// <param name="kea"></param>
-	public static explicit operator char (Key kea) => (char)kea.AsRune.Value;
+	public static explicit operator uint (Key kea) => (uint)kea.KeyCode;
 
 
 	/// <summary>
 	/// <summary>
-	/// Explicitly cast <see cref="Key"/> to a <see cref="KeyCode"/>. The conversion is lossy. 
+	/// Explicitly cast <see cref="Key"/> to a <see cref="KeyCode"/>. The conversion is lossy because properties
+	/// such as <see cref="Handled"/> are not encoded in <see cref="KeyCode"/>.
 	/// </summary>
 	/// </summary>
 	/// <param name="key"></param>
 	/// <param name="key"></param>
 	public static explicit operator KeyCode (Key key) => key.KeyCode;
 	public static explicit operator KeyCode (Key key) => key.KeyCode;
@@ -365,6 +389,7 @@ public class Key : EventArgs, IEquatable<Key> {
 	public override int GetHashCode () => (int)KeyCode;
 	public override int GetHashCode () => (int)KeyCode;
 
 
 	/// <summary>
 	/// <summary>
+	/// Compares two <see cref="Key"/>s for equality.
 	/// </summary>
 	/// </summary>
 	/// <param name="a"></param>
 	/// <param name="a"></param>
 	/// <param name="b"></param>
 	/// <param name="b"></param>
@@ -372,6 +397,7 @@ public class Key : EventArgs, IEquatable<Key> {
 	public static bool operator == (Key a, Key b) => a?.KeyCode == b?.KeyCode;
 	public static bool operator == (Key a, Key b) => a?.KeyCode == b?.KeyCode;
 
 
 	/// <summary>
 	/// <summary>
+	/// Compares two <see cref="Key"/>s for not equality.
 	/// </summary>
 	/// </summary>
 	/// <param name="a"></param>
 	/// <param name="a"></param>
 	/// <param name="b"></param>
 	/// <param name="b"></param>
@@ -427,15 +453,15 @@ public class Key : EventArgs, IEquatable<Key> {
 		var baseKey = key & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask;
 		var baseKey = key & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask;
 
 
 		if (!key.HasFlag (KeyCode.ShiftMask) && baseKey is >= KeyCode.A and <= KeyCode.Z) {
 		if (!key.HasFlag (KeyCode.ShiftMask) && baseKey is >= KeyCode.A and <= KeyCode.Z) {
-			return ((char)(key + 32)).ToString ();
+			return ((Rune)(uint)(key + 32)).ToString ();
 		}
 		}
 
 
-		if (key is >= KeyCode.Space and < KeyCode.A) {
-			return ((char)key).ToString ();
+		if (key is > KeyCode.Space and < KeyCode.A) {
+			return ((Rune)(uint)key).ToString ();
 		}
 		}
 
 
 		string keyName = Enum.GetName (typeof (KeyCode), key);
 		string keyName = Enum.GetName (typeof (KeyCode), key);
-		return !string.IsNullOrEmpty (keyName) ? keyName : ((char)key).ToString ();
+		return !string.IsNullOrEmpty (keyName) ? keyName : ((Rune)(uint)key).ToString ();
 	}
 	}
 
 
 
 
@@ -454,7 +480,7 @@ public class Key : EventArgs, IEquatable<Key> {
 	/// <returns>The formatted string. If the key is a printable character, it will be returned as a string. Otherwise, the key name will be returned.</returns>
 	/// <returns>The formatted string. If the key is a printable character, it will be returned as a string. Otherwise, the key name will be returned.</returns>
 	public static string ToString (KeyCode key, Rune separator)
 	public static string ToString (KeyCode key, Rune separator)
 	{
 	{
-		if (key is KeyCode.Null || (key & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask) == 0) {
+		if (key is KeyCode.Null) {
 			// Same as Key.IsValid
 			// Same as Key.IsValid
 			return @"Null";
 			return @"Null";
 		}
 		}
@@ -489,21 +515,19 @@ public class Key : EventArgs, IEquatable<Key> {
 			}
 			}
 		}
 		}
 
 
-		string result = sb.ToString ();
-		result = TrimEndRune (result, separator);
-		return result;
+		return TrimEndSeparator (sb.ToString (), separator);
 	}
 	}
 
 
-	static string TrimEndRune (string input, Rune runeToTrim)
+	static string TrimEndSeparator (string input, Rune separator)
 	{
 	{
-		// Convert the Rune to a string (which may be one or two chars)
-		string runeString = runeToTrim.ToString ();
+		// Trim the trailing separator (+). Unless there are two separators at the end.
+		// "+" (don't trim)
+		// "Ctrl+" (trim)
+		// "Ctrl++" (trim)
 
 
-		if (input.EndsWith (runeString)) {
-			// Remove the rune from the end of the string
-			return input.Substring (0, input.Length - runeString.Length);
+		if (input.Length > 1 && new Rune(input [^1]) == separator && new Rune(input [^2]) != separator) {
+			return input [..^1];
 		}
 		}
-
 		return input;
 		return input;
 	}
 	}
 
 
@@ -543,13 +567,13 @@ public class Key : EventArgs, IEquatable<Key> {
 		if (parts.Length == 1) {
 		if (parts.Length == 1) {
 			switch (parts [0]) {
 			switch (parts [0]) {
 			case "Ctrl":
 			case "Ctrl":
-				key = new Key (KeyCode.CtrlKey);
+				key = new Key (KeyCode.CtrlMask);
 				return true;
 				return true;
 			case "Alt":
 			case "Alt":
-				key = new Key (KeyCode.AltKey);
+				key = new Key (KeyCode.AltMask);
 				return true;
 				return true;
 			case "Shift":
 			case "Shift":
-				key = new Key (KeyCode.ShiftKey);
+				key = new Key (KeyCode.ShiftMask);
 				return true;
 				return true;
 			}
 			}
 		}
 		}
@@ -655,26 +679,6 @@ public class Key : EventArgs, IEquatable<Key> {
 	/// </summary>
 	/// </summary>
 	public static Key Clear => new (KeyCode.Clear);
 	public static Key Clear => new (KeyCode.Clear);
 
 
-	/// <summary>
-	/// The <see cref="Key"/> object for the Shift key.
-	/// </summary>
-	public static Key Shift => new (KeyCode.ShiftKey);
-
-	/// <summary>
-	/// The <see cref="Key"/> object for the Ctrl key.
-	/// </summary>
-	public static Key Ctrl => new (KeyCode.CtrlKey);
-
-	/// <summary>
-	/// The <see cref="Key"/> object for the Alt key.
-	/// </summary>
-	public static Key Alt => new (KeyCode.AltKey);
-
-	/// <summary>
-	/// The <see cref="Key"/> object for the CapsLock key.
-	/// </summary>
-	public static Key CapsLock => new (KeyCode.CapsLock);
-
 	/// <summary>
 	/// <summary>
 	/// The <see cref="Key"/> object for the Escape key.
 	/// The <see cref="Key"/> object for the Escape key.
 	/// </summary>
 	/// </summary>
@@ -913,12 +917,12 @@ public class Key : EventArgs, IEquatable<Key> {
 	/// <summary>
 	/// <summary>
 	/// The <see cref="Key"/> object for Insert Character key.
 	/// The <see cref="Key"/> object for Insert Character key.
 	/// </summary>
 	/// </summary>
-	public static Key InsertChar => new (KeyCode.InsertChar);
+	public static Key InsertChar => new (KeyCode.Insert);
 
 
 	/// <summary>
 	/// <summary>
 	/// The <see cref="Key"/> object for Delete Character key.
 	/// The <see cref="Key"/> object for Delete Character key.
 	/// </summary>
 	/// </summary>
-	public static Key DeleteChar => new (KeyCode.DeleteChar);
+	public static Key DeleteChar => new (KeyCode.Delete);
 
 
 	/// <summary>
 	/// <summary>
 	/// The <see cref="Key"/> object for Print Screen key.
 	/// The <see cref="Key"/> object for Print Screen key.

+ 1 - 0
Terminal.Gui/Terminal.Gui.csproj

@@ -113,5 +113,6 @@
     <EnableSourceLink>true</EnableSourceLink>
     <EnableSourceLink>true</EnableSourceLink>
     <!--<DebugType>Embedded</DebugType>-->
     <!--<DebugType>Embedded</DebugType>-->
     <Authors>Miguel de Icaza, Tig Kindel (@tig), @BDisp</Authors>
     <Authors>Miguel de Icaza, Tig Kindel (@tig), @BDisp</Authors>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
   </PropertyGroup>
 </Project>
 </Project>

+ 179 - 182
Terminal.Gui/Text/CollectionNavigatorBase.cs

@@ -1,217 +1,214 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Text;
+
+namespace Terminal.Gui; 
+
+/// <summary>
+/// Navigates a collection of items using keystrokes. The keystrokes are used to build a search string. 
+/// The <see cref="SearchString"/> is used to find the next item in the collection that matches the search string
+/// when <see cref="GetNextMatchingItem(int, char)"/> is called.
+/// <para>
+/// If the user types keystrokes that can't be found in the collection, 
+/// the search string is cleared and the next item is found that starts with the last keystroke.
+/// </para>
+/// <para>
+/// If the user pauses keystrokes for a short time (see <see cref="TypingDelay"/>), the search string is cleared.
+/// </para>
+/// </summary>
+public abstract class CollectionNavigatorBase {
+	DateTime _lastKeystroke = DateTime.Now;
 
 
-namespace Terminal.Gui {
 	/// <summary>
 	/// <summary>
-	/// Navigates a collection of items using keystrokes. The keystrokes are used to build a search string. 
-	/// The <see cref="SearchString"/> is used to find the next item in the collection that matches the search string
-	/// when <see cref="GetNextMatchingItem(int, char)"/> is called.
-	/// <para>
-	/// If the user types keystrokes that can't be found in the collection, 
-	/// the search string is cleared and the next item is found that starts with the last keystroke.
-	/// </para>
-	/// <para>
-	/// If the user pauses keystrokes for a short time (see <see cref="TypingDelay"/>), the search string is cleared.
-	/// </para>
+	/// Gets or sets the number of milliseconds to delay before clearing the search string. The delay is
+	/// reset on each call to <see cref="GetNextMatchingItem(int, char)"/>. The default is 500ms.
 	/// </summary>
 	/// </summary>
-	public abstract class CollectionNavigatorBase {
-
-		DateTime lastKeystroke = DateTime.Now;
-		/// <summary>
-		/// Gets or sets the number of milliseconds to delay before clearing the search string. The delay is
-		/// reset on each call to <see cref="GetNextMatchingItem(int, char)"/>. The default is 500ms.
-		/// </summary>
-		public int TypingDelay { get; set; } = 500;
-
-		/// <summary>
-		/// The compararer function to use when searching the collection.
-		/// </summary>
-		public StringComparer Comparer { get; set; } = StringComparer.InvariantCultureIgnoreCase;
-
-		/// <summary>
-		/// This event is invoked when <see cref="SearchString"/>  changes. Useful for debugging.
-		/// </summary>
-		public event EventHandler<KeystrokeNavigatorEventArgs> SearchStringChanged;
-
-		private string _searchString = "";
-		/// <summary>
-		/// Gets the current search string. This includes the set of keystrokes that have been pressed
-		/// since the last unsuccessful match or after <see cref="TypingDelay"/>) milliseconds. Useful for debugging.
-		/// </summary>
-		public string SearchString {
-			get => _searchString;
-			private set {
-				_searchString = value;
-				OnSearchStringChanged (new KeystrokeNavigatorEventArgs (value));
-			}
-		}
+	public int TypingDelay { get; set; } = 500;
 
 
-		/// <summary>
-		/// Invoked when the <see cref="SearchString"/> changes. Useful for debugging. Invokes the <see cref="SearchStringChanged"/> event.
-		/// </summary>
-		/// <param name="e"></param>
-		public virtual void OnSearchStringChanged (KeystrokeNavigatorEventArgs e)
-		{
-			SearchStringChanged?.Invoke (this, e);
-		}
+	/// <summary>
+	/// The comparer function to use when searching the collection.
+	/// </summary>
+	public StringComparer Comparer { get; set; } = StringComparer.InvariantCultureIgnoreCase;
 
 
-		/// <summary>
-		/// Gets the index of the next item in the collection that matches the current <see cref="SearchString"/> plus the provided character (typically
-		/// from a key press).
-		/// </summary>
-		/// <param name="currentIndex">The index in the collection to start the search from.</param>
-		/// <param name="keyStruck">The character of the key the user pressed.</param>
-		/// <returns>The index of the item that matches what the user has typed. 
-		/// Returns <see langword="-1"/> if no item in the collection matched.</returns>
-		public int GetNextMatchingItem (int currentIndex, char keyStruck)
-		{
-			if (!char.IsControl (keyStruck)) {
-
-				// maybe user pressed 'd' and now presses 'd' again.
-				// a candidate search is things that begin with "dd"
-				// but if we find none then we must fallback on cycling
-				// d instead and discard the candidate state
-				string candidateState = "";
-
-				// is it a second or third (etc) keystroke within a short time
-				if (SearchString.Length > 0 && DateTime.Now - lastKeystroke < TimeSpan.FromMilliseconds (TypingDelay)) {
-					// "dd" is a candidate
-					candidateState = SearchString + keyStruck;
-				} else {
-					// its a fresh keystroke after some time
-					// or its first ever key press
-					SearchString = new string (keyStruck, 1);
-				}
+	/// <summary>
+	/// This event is invoked when <see cref="SearchString"/>  changes. Useful for debugging.
+	/// </summary>
+	public event EventHandler<KeystrokeNavigatorEventArgs> SearchStringChanged;
 
 
-				var idxCandidate = GetNextMatchingItem (currentIndex, candidateState,
-					// prefer not to move if there are multiple characters e.g. "ca" + 'r' should stay on "car" and not jump to "cart"
-					candidateState.Length > 1);
+	string _searchString = "";
 
 
-				if (idxCandidate != -1) {
-					// found "dd" so candidate searchstring is accepted
-					lastKeystroke = DateTime.Now;
-					SearchString = candidateState;
-					return idxCandidate;
-				}
+	/// <summary>
+	/// Gets the current search string. This includes the set of keystrokes that have been pressed
+	/// since the last unsuccessful match or after <see cref="TypingDelay"/>) milliseconds. Useful for debugging.
+	/// </summary>
+	public string SearchString {
+		get => _searchString;
+		private set {
+			_searchString = value;
+			OnSearchStringChanged (new KeystrokeNavigatorEventArgs (value));
+		}
+	}
 
 
-				//// nothing matches "dd" so discard it as a candidate
-				//// and just cycle "d" instead
-				lastKeystroke = DateTime.Now;
-				idxCandidate = GetNextMatchingItem (currentIndex, candidateState);
-
-				// if a match wasn't found, the user typed a 'wrong' key in their search ("can" + 'z'
-				// instead of "can" + 'd').
-				if (SearchString.Length > 1 && idxCandidate == -1) {
-					// ignore it since we're still within the typing delay
-					// don't add it to SearchString either
-					return currentIndex;
-				}
+	/// <summary>
+	/// Invoked when the <see cref="SearchString"/> changes. Useful for debugging. Invokes the <see cref="SearchStringChanged"/> event.
+	/// </summary>
+	/// <param name="e"></param>
+	public virtual void OnSearchStringChanged (KeystrokeNavigatorEventArgs e) => SearchStringChanged?.Invoke (this, e);
 
 
-				// if no changes to current state manifested
-				if (idxCandidate == currentIndex || idxCandidate == -1) {
-					// clear history and treat as a fresh letter
-					ClearSearchString ();
+	/// <summary>
+	/// Gets the index of the next item in the collection that matches the current <see cref="SearchString"/> plus the provided character (typically
+	/// from a key press).
+	/// </summary>
+	/// <param name="currentIndex">The index in the collection to start the search from.</param>
+	/// <param name="keyStruck">The character of the key the user pressed.</param>
+	/// <returns>The index of the item that matches what the user has typed. 
+	/// Returns <see langword="-1"/> if no item in the collection matched.</returns>
+	public int GetNextMatchingItem (int currentIndex, char keyStruck)
+	{
+		if (!char.IsControl (keyStruck)) {
+
+			// maybe user pressed 'd' and now presses 'd' again.
+			// a candidate search is things that begin with "dd"
+			// but if we find none then we must fallback on cycling
+			// d instead and discard the candidate state
+			string candidateState = "";
+
+			// is it a second or third (etc) keystroke within a short time
+			if (SearchString.Length > 0 && DateTime.Now - _lastKeystroke < TimeSpan.FromMilliseconds (TypingDelay)) {
+				// "dd" is a candidate
+				candidateState = SearchString + keyStruck;
+			} else {
+				// its a fresh keystroke after some time
+				// or its first ever key press
+				SearchString = new string (keyStruck, 1);
+			}
 
 
-					// match on the fresh letter alone
-					SearchString = new string (keyStruck, 1);
-					idxCandidate = GetNextMatchingItem (currentIndex, SearchString);
-					return idxCandidate == -1 ? currentIndex : idxCandidate;
-				}
+			int idxCandidate = GetNextMatchingItem (currentIndex, candidateState,
+				// prefer not to move if there are multiple characters e.g. "ca" + 'r' should stay on "car" and not jump to "cart"
+				candidateState.Length > 1);
 
 
-				// Found another "d" or just leave index as it was
+			if (idxCandidate != -1) {
+				// found "dd" so candidate search string is accepted
+				_lastKeystroke = DateTime.Now;
+				SearchString = candidateState;
 				return idxCandidate;
 				return idxCandidate;
+			}
 
 
-			} else {
-				// clear state because keypress was a control char
-				ClearSearchString ();
+			//// nothing matches "dd" so discard it as a candidate
+			//// and just cycle "d" instead
+			_lastKeystroke = DateTime.Now;
+			idxCandidate = GetNextMatchingItem (currentIndex, candidateState);
 
 
-				// control char indicates no selection
-				return -1;
+			// if a match wasn't found, the user typed a 'wrong' key in their search ("can" + 'z'
+			// instead of "can" + 'd').
+			if (SearchString.Length > 1 && idxCandidate == -1) {
+				// ignore it since we're still within the typing delay
+				// don't add it to SearchString either
+				return currentIndex;
 			}
 			}
-		}
 
 
-		/// <summary>
-		/// Gets the index of the next item in the collection that matches <paramref name="search"/>. 
-		/// </summary>
-		/// <param name="currentIndex">The index in the collection to start the search from.</param>
-		/// <param name="search">The search string to use.</param>
-		/// <param name="minimizeMovement">Set to <see langword="true"/> to stop the search on the first match
-		/// if there are multiple matches for <paramref name="search"/>.
-		/// e.g. "ca" + 'r' should stay on "car" and not jump to "cart". If <see langword="false"/> (the default), 
-		/// the next matching item will be returned, even if it is above in the collection.
-		/// </param>
-		/// <returns>The index of the next matching item or <see langword="-1"/> if no match was found.</returns>
-		internal int GetNextMatchingItem (int currentIndex, string search, bool minimizeMovement = false)
-		{
-			if (string.IsNullOrEmpty (search)) {
-				return -1;
+			// if no changes to current state manifested
+			if (idxCandidate == currentIndex || idxCandidate == -1) {
+				// clear history and treat as a fresh letter
+				ClearSearchString ();
+
+				// match on the fresh letter alone
+				SearchString = new string (keyStruck, 1);
+				idxCandidate = GetNextMatchingItem (currentIndex, SearchString);
+				return idxCandidate == -1 ? currentIndex : idxCandidate;
 			}
 			}
 
 
-			var collectionLength = GetCollectionLength ();
+			// Found another "d" or just leave index as it was
+			return idxCandidate;
 
 
-			if (currentIndex != -1 && currentIndex < collectionLength && IsMatch (search, ElementAt (currentIndex))) {
-				// we are already at a match
-				if (minimizeMovement) {
-					// if we would rather not jump around (e.g. user is typing lots of text to get this match)
-					return currentIndex;
-				}
+		} else {
+			// clear state because keypress was a control char
+			ClearSearchString ();
 
 
-				for (int i = 1; i < collectionLength; i++) {
-					//circular
-					var idxCandidate = (i + currentIndex) % collectionLength;
-					if (IsMatch (search, ElementAt (idxCandidate))) {
-						return idxCandidate;
-					}
-				}
+			// control char indicates no selection
+			return -1;
+		}
+	}
+
+	/// <summary>
+	/// Gets the index of the next item in the collection that matches <paramref name="search"/>. 
+	/// </summary>
+	/// <param name="currentIndex">The index in the collection to start the search from.</param>
+	/// <param name="search">The search string to use.</param>
+	/// <param name="minimizeMovement">Set to <see langword="true"/> to stop the search on the first match
+	/// if there are multiple matches for <paramref name="search"/>.
+	/// e.g. "ca" + 'r' should stay on "car" and not jump to "cart". If <see langword="false"/> (the default), 
+	/// the next matching item will be returned, even if it is above in the collection.
+	/// </param>
+	/// <returns>The index of the next matching item or <see langword="-1"/> if no match was found.</returns>
+	internal int GetNextMatchingItem (int currentIndex, string search, bool minimizeMovement = false)
+	{
+		if (string.IsNullOrEmpty (search)) {
+			return -1;
+		}
 
 
-				// nothing else starts with the search term
+		int collectionLength = GetCollectionLength ();
+
+		if (currentIndex != -1 && currentIndex < collectionLength && IsMatch (search, ElementAt (currentIndex))) {
+			// we are already at a match
+			if (minimizeMovement) {
+				// if we would rather not jump around (e.g. user is typing lots of text to get this match)
 				return currentIndex;
 				return currentIndex;
-			} else {
-				// search terms no longer match the current selection or there is none
-				for (int i = 0; i < collectionLength; i++) {
-					if (IsMatch (search, ElementAt (i))) {
-						return i;
-					}
+			}
+
+			for (int i = 1; i < collectionLength; i++) {
+				//circular
+				int idxCandidate = (i + currentIndex) % collectionLength;
+				if (IsMatch (search, ElementAt (idxCandidate))) {
+					return idxCandidate;
 				}
 				}
+			}
 
 
-				// Nothing matches
-				return -1;
+			// nothing else starts with the search term
+			return currentIndex;
+		} else {
+			// search terms no longer match the current selection or there is none
+			for (int i = 0; i < collectionLength; i++) {
+				if (IsMatch (search, ElementAt (i))) {
+					return i;
+				}
 			}
 			}
+
+			// Nothing matches
+			return -1;
 		}
 		}
+	}
 
 
-		/// <summary>
-		/// Return the number of elements in the collection
-		/// </summary>
-		protected abstract int GetCollectionLength ();
+	/// <summary>
+	/// Return the number of elements in the collection
+	/// </summary>
+	protected abstract int GetCollectionLength ();
 
 
-		private bool IsMatch (string search, object value)
-		{
-			return value?.ToString ().StartsWith (search, StringComparison.InvariantCultureIgnoreCase) ?? false;
-		}
+	bool IsMatch (string search, object value) => value?.ToString ().StartsWith (search, StringComparison.InvariantCultureIgnoreCase) ?? false;
 
 
-		/// <summary>
-		/// Returns the collection being navigated element at <paramref name="idx"/>.
-		/// </summary>
-		/// <returns></returns>
-		protected abstract object ElementAt (int idx);
+	/// <summary>
+	/// Returns the collection being navigated element at <paramref name="idx"/>.
+	/// </summary>
+	/// <returns></returns>
+	protected abstract object ElementAt (int idx);
 
 
-		private void ClearSearchString ()
-		{
-			SearchString = "";
-			lastKeystroke = DateTime.Now;
-		}
+	void ClearSearchString ()
+	{
+		SearchString = "";
+		_lastKeystroke = DateTime.Now;
+	}
 
 
-		/// <summary>
-		/// Returns true if <paramref name="a"/> is a searchable key
-		/// (e.g. letters, numbers, etc) that are valid to pass to this
-		/// class for search filtering.
-		/// </summary>
-		/// <param name="a"></param>
-		/// <returns></returns>
-		public static bool IsCompatibleKey (Key a)
-		{
-			return !a.IsAlt && !a.IsCtrl;
-		}
+	/// <summary>
+	/// Returns true if <paramref name="a"/> is a searchable key
+	/// (e.g. letters, numbers, etc) that are valid to pass to this
+	/// class for search filtering.
+	/// </summary>
+	/// <param name="a"></param>
+	/// <returns></returns>
+	public static bool IsCompatibleKey (Key a)
+	{
+		var rune = a.AsRune;
+		return rune != default && !Rune.IsControl (rune);
 	}
 	}
-}
+}

+ 9 - 3
Terminal.Gui/View/ViewKeyboard.cs

@@ -330,6 +330,10 @@ public partial class View {
 		}
 		}
 
 
 		// During (this is what can be cancelled)
 		// During (this is what can be cancelled)
+		InvokingKeyBindings?.Invoke (this, keyEvent);
+		if (keyEvent.Handled) {
+			return true;
+		}
 		var handled = OnInvokingKeyBindings (keyEvent);
 		var handled = OnInvokingKeyBindings (keyEvent);
 		if (handled != null && (bool)handled) {
 		if (handled != null && (bool)handled) {
 			return true;
 			return true;
@@ -553,9 +557,11 @@ public partial class View {
 	{
 	{
 		// fire event
 		// fire event
 		// BUGBUG: KeyEventArgs doesn't include scope, so the event never sees it.
 		// BUGBUG: KeyEventArgs doesn't include scope, so the event never sees it.
-		InvokingKeyBindings?.Invoke (this, keyEvent);
-		if (keyEvent.Handled) {
-			return true;
+		if (keyEvent.Scope == KeyBindingScope.Application || keyEvent.Scope == KeyBindingScope.HotKey) {
+			InvokingKeyBindings?.Invoke (this, keyEvent);
+			if (keyEvent.Handled) {
+				return true;
+			}
 		}
 		}
 
 
 		// * If no key binding was found, `InvokeKeyBindings` returns `null`.
 		// * If no key binding was found, `InvokeKeyBindings` returns `null`.

+ 1 - 1
Terminal.Gui/Views/DateField.cs

@@ -92,7 +92,7 @@ public class DateField : TextField {
 		AddCommand (Command.Right, () => MoveRight ());
 		AddCommand (Command.Right, () => MoveRight ());
 
 
 		// Default keybindings for this view
 		// Default keybindings for this view
-		KeyBindings.Add (KeyCode.DeleteChar, Command.DeleteCharRight);
+		KeyBindings.Add (KeyCode.Delete, Command.DeleteCharRight);
 		KeyBindings.Add (Key.D.WithCtrl, Command.DeleteCharRight);
 		KeyBindings.Add (Key.D.WithCtrl, Command.DeleteCharRight);
 
 
 		KeyBindings.Add (Key.Delete, Command.DeleteCharLeft);
 		KeyBindings.Add (Key.Delete, Command.DeleteCharLeft);

+ 1 - 1
Terminal.Gui/Views/FileDialog.cs

@@ -963,7 +963,7 @@ namespace Terminal.Gui {
 				return this.history.Forward ();
 				return this.history.Forward ();
 			}
 			}
 
 
-			if (keyEvent.KeyCode == KeyCode.DeleteChar) {
+			if (keyEvent.KeyCode == KeyCode.Delete) {
 
 
 				Delete ();
 				Delete ();
 				return true;
 				return true;

+ 3 - 4
Terminal.Gui/Views/TextField.cs

@@ -143,10 +143,9 @@ namespace Terminal.Gui {
 
 
 			// Default keybindings for this view
 			// Default keybindings for this view
 			// We follow this as closely as possible: https://en.wikipedia.org/wiki/Table_of_keyboard_shortcuts
 			// We follow this as closely as possible: https://en.wikipedia.org/wiki/Table_of_keyboard_shortcuts
-			KeyBindings.Add (KeyCode.DeleteChar, Command.DeleteCharRight);
+			KeyBindings.Add (KeyCode.Delete, Command.DeleteCharRight);
 			KeyBindings.Add (KeyCode.D | KeyCode.CtrlMask, Command.DeleteCharRight);
 			KeyBindings.Add (KeyCode.D | KeyCode.CtrlMask, Command.DeleteCharRight);
 
 
-			KeyBindings.Add (KeyCode.Delete, Command.DeleteCharLeft);
 			KeyBindings.Add (KeyCode.Backspace, Command.DeleteCharLeft);
 			KeyBindings.Add (KeyCode.Backspace, Command.DeleteCharLeft);
 
 
 			KeyBindings.Add (KeyCode.Home | KeyCode.ShiftMask, Command.LeftHomeExtend);
 			KeyBindings.Add (KeyCode.Home | KeyCode.ShiftMask, Command.LeftHomeExtend);
@@ -201,9 +200,9 @@ namespace Terminal.Gui {
 			KeyBindings.Add (KeyCode.CursorDown | KeyCode.CtrlMask, Command.WordRight);
 			KeyBindings.Add (KeyCode.CursorDown | KeyCode.CtrlMask, Command.WordRight);
 			KeyBindings.Add ((KeyCode)((int)'F' + KeyCode.AltMask), Command.WordRight);
 			KeyBindings.Add ((KeyCode)((int)'F' + KeyCode.AltMask), Command.WordRight);
 
 
-			KeyBindings.Add (KeyCode.DeleteChar | KeyCode.CtrlMask, Command.KillWordForwards);
+			KeyBindings.Add (KeyCode.Delete | KeyCode.CtrlMask, Command.KillWordForwards);
 			KeyBindings.Add (KeyCode.Backspace | KeyCode.CtrlMask, Command.KillWordBackwards);
 			KeyBindings.Add (KeyCode.Backspace | KeyCode.CtrlMask, Command.KillWordBackwards);
-			KeyBindings.Add (KeyCode.InsertChar, Command.ToggleOverwrite);
+			KeyBindings.Add (KeyCode.Insert, Command.ToggleOverwrite);
 			KeyBindings.Add (KeyCode.C | KeyCode.CtrlMask, Command.Copy);
 			KeyBindings.Add (KeyCode.C | KeyCode.CtrlMask, Command.Copy);
 			KeyBindings.Add (KeyCode.X | KeyCode.CtrlMask, Command.Cut);
 			KeyBindings.Add (KeyCode.X | KeyCode.CtrlMask, Command.Cut);
 			KeyBindings.Add (KeyCode.V | KeyCode.CtrlMask, Command.Paste);
 			KeyBindings.Add (KeyCode.V | KeyCode.CtrlMask, Command.Paste);

+ 1 - 1
Terminal.Gui/Views/TextValidateField.cs

@@ -404,7 +404,7 @@ namespace Terminal.Gui {
 			KeyBindings.Add (KeyCode.End, Command.RightEnd);
 			KeyBindings.Add (KeyCode.End, Command.RightEnd);
 
 
 			KeyBindings.Add (KeyCode.Delete, Command.DeleteCharRight);
 			KeyBindings.Add (KeyCode.Delete, Command.DeleteCharRight);
-			KeyBindings.Add (KeyCode.DeleteChar, Command.DeleteCharRight);
+			KeyBindings.Add (KeyCode.Delete, Command.DeleteCharRight);
 
 
 			KeyBindings.Add (KeyCode.Backspace, Command.DeleteCharLeft);
 			KeyBindings.Add (KeyCode.Backspace, Command.DeleteCharLeft);
 			KeyBindings.Add (KeyCode.CursorLeft, Command.Left);
 			KeyBindings.Add (KeyCode.CursorLeft, Command.Left);

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1307 - 1317
Terminal.Gui/Views/TextView.cs


+ 1 - 1
Terminal.Gui/Views/TimeField.cs

@@ -87,7 +87,7 @@ namespace Terminal.Gui {
 			AddCommand (Command.Right, () => MoveRight ());
 			AddCommand (Command.Right, () => MoveRight ());
 
 
 			// Default keybindings for this view
 			// Default keybindings for this view
-			KeyBindings.Add (KeyCode.DeleteChar, Command.DeleteCharRight);
+			KeyBindings.Add (KeyCode.Delete, Command.DeleteCharRight);
 			KeyBindings.Add (KeyCode.D | KeyCode.CtrlMask, Command.DeleteCharRight);
 			KeyBindings.Add (KeyCode.D | KeyCode.CtrlMask, Command.DeleteCharRight);
 
 
 			KeyBindings.Add (KeyCode.Delete, Command.DeleteCharLeft);
 			KeyBindings.Add (KeyCode.Delete, Command.DeleteCharLeft);

+ 3 - 3
UICatalog/Scenarios/CsvEditor.cs

@@ -466,9 +466,9 @@ public class CsvEditor : Scenario {
 
 
 	}
 	}
 
 
-	void TableViewKeyPress (object sender, Key e)
-	{
-		if (e.KeyCode == KeyCode.DeleteChar) {
+		private void TableViewKeyPress (object sender, Key e)
+		{
+			if (e.KeyCode == KeyCode.Delete) {
 
 
 			if (tableView.FullRowSelect) {
 			if (tableView.FullRowSelect) {
 				// Delete button deletes all rows when in full row mode
 				// Delete button deletes all rows when in full row mode

+ 1 - 1
UICatalog/Scenarios/InteractiveTree.cs

@@ -45,7 +45,7 @@ namespace UICatalog.Scenarios {
 
 
 		private void TreeView_KeyPress (object sender, Key obj)
 		private void TreeView_KeyPress (object sender, Key obj)
 		{
 		{
-			if (obj.KeyCode == KeyCode.DeleteChar) {
+			if (obj.KeyCode == KeyCode.Delete) {
 
 
 				var toDelete = treeView.SelectedObject;
 				var toDelete = treeView.SelectedObject;
 
 

+ 1 - 1
UICatalog/Scenarios/ListColumns.cs

@@ -154,7 +154,7 @@ namespace UICatalog.Scenarios {
 
 
 		private void TableViewKeyPress (object sender, Key e)
 		private void TableViewKeyPress (object sender, Key e)
 		{
 		{
-			if (e.KeyCode == KeyCode.DeleteChar) {
+			if (e.KeyCode == KeyCode.Delete) {
 
 
 				// set all selected cells to null
 				// set all selected cells to null
 				foreach (var pt in listColView.GetAllSelectedCells ()) {
 				foreach (var pt in listColView.GetAllSelectedCells ()) {

+ 1 - 1
UICatalog/Scenarios/TableEditor.cs

@@ -391,7 +391,7 @@ namespace UICatalog.Scenarios {
 				return;
 				return;
 			}
 			}
 
 
-			if (e.KeyCode == KeyCode.DeleteChar) {
+			if (e.KeyCode == KeyCode.Delete) {
 
 
 				if (tableView.FullRowSelect) {
 				if (tableView.FullRowSelect) {
 					// Delete button deletes all rows when in full row mode
 					// Delete button deletes all rows when in full row mode

+ 55 - 69
UICatalog/Scenarios/VkeyPacketSimulator.cs

@@ -5,12 +5,12 @@ using System.Threading.Tasks;
 using Terminal.Gui;
 using Terminal.Gui;
 using Terminal.Gui.ConsoleDrivers;
 using Terminal.Gui.ConsoleDrivers;
 
 
-namespace UICatalog.Scenarios; 
+namespace UICatalog.Scenarios;
 
 
 [ScenarioMetadata ("VkeyPacketSimulator", "Simulates the Virtual Key Packet")]
 [ScenarioMetadata ("VkeyPacketSimulator", "Simulates the Virtual Key Packet")]
 [ScenarioCategory ("Mouse and Keyboard")]
 [ScenarioCategory ("Mouse and Keyboard")]
 public class VkeyPacketSimulator : Scenario {
 public class VkeyPacketSimulator : Scenario {
-	List<int> _keyboardStrokes = new ();
+	List<KeyCode> _keyboardStrokes = new ();
 	bool _outputStarted = false;
 	bool _outputStarted = false;
 	bool _wasUnknown = false;
 	bool _wasUnknown = false;
 	static ManualResetEventSlim _stopOutput = new (false);
 	static ManualResetEventSlim _stopOutput = new (false);
@@ -94,54 +94,55 @@ public class VkeyPacketSimulator : Scenario {
 
 
 		// Detect unknown keys and reject them.
 		// Detect unknown keys and reject them.
 		tvOutput.KeyDown += (s, e) => {
 		tvOutput.KeyDown += (s, e) => {
-			//System.Diagnostics.Debug.WriteLine ($"Output - KeyDown: {e.Key}");
-			//e.Handled = true;
-			if (e == Key.Empty) {
+			//System.Diagnostics.Debug.WriteLine ($"Output - KeyDown: {e.KeyCode}");
+			if (e.NoAlt.NoCtrl.NoShift == KeyCode.Null) {
 				_wasUnknown = true;
 				_wasUnknown = true;
+				e.Handled = true;
+				return;
 			}
 			}
-		};
 
 
-		tvOutput.KeyUp += (s, e) => {
 			//System.Diagnostics.Debug.WriteLine ($"Output - KeyPress - _keyboardStrokes: {_keyboardStrokes.Count}");
 			//System.Diagnostics.Debug.WriteLine ($"Output - KeyPress - _keyboardStrokes: {_keyboardStrokes.Count}");
-			if (_outputStarted && _keyboardStrokes.Count > 0) {
-				//// TODO: Tig: I don't understand what this is trying to do
-				//if (!tvOutput.ProcessKeyDown (e)) {
-				//	Application.Invoke (() => {
-				//		MessageBox.Query ("Keys", $"'{KeyEventArgs.ToString (e.ConsoleDriverKey, MenuBar.ShortcutDelimiter)}' pressed!", "Ok");
-				//	});
-				//}
-				e.Handled = true;
-				_stopOutput.Set ();
+			if (_outputStarted) {
+				// If the key wasn't handled by the TextView will popup a Dialog with the keys pressed.
+				var handled = tvOutput.OnInvokingKeyBindings (e);
+				if (handled == null || handled == false) {
+					if (!tvOutput.OnProcessKeyDown (e)) {
+						Application.Invoke (() => MessageBox.Query ("Keys", $"'{Key.ToString (e.KeyCode, MenuBar.ShortcutDelimiter)}' pressed!", "Ok"));
+					}
+				}
 			}
 			}
-			//System.Diagnostics.Debug.WriteLine ($"Output - KeyPress - _keyboardStrokes: {_keyboardStrokes.Count}");
+			e.Handled = true;
+			_stopOutput.Set ();
 		};
 		};
 
 
 		Win.Add (tvOutput);
 		Win.Add (tvOutput);
 
 
-		Key unknownChar = null;
+		tvInput.KeyDown += (s, e) => {
+			//System.Diagnostics.Debug.WriteLine ($"Input - KeyDown: {e.KeyCode.Key}");
+			if (e.KeyCode == Key.Empty) {
+				_wasUnknown = true;
+				e.Handled = true;
+			} else {
+				_wasUnknown = false;
+			}
+		};
 
 
-		tvInput.KeyUp += (s, e) => {
-			//System.Diagnostics.Debug.WriteLine ($"Input - KeyUp: {e.Key}");
-			//var ke = e;
+		tvInput.InvokingKeyBindings += (s, e) => {
+			var ev = e;
+			//System.Diagnostics.Debug.WriteLine ($"Input - KeyPress: {ev}");
+			//System.Diagnostics.Debug.WriteLine ($"Input - KeyPress - _keyboardStrokes: {_keyboardStrokes.Count}");
 
 
-			if (e == Key.Empty) {
+			if (!e.IsValid) {
 				_wasUnknown = true;
 				_wasUnknown = true;
 				e.Handled = true;
 				e.Handled = true;
 				return;
 				return;
 			}
 			}
-			if (_wasUnknown && _keyboardStrokes.Count == 1) {
-				_wasUnknown = false;
-			} else if (_wasUnknown && char.IsLetter ((char)e.KeyCode)) {
-				_wasUnknown = false;
-			}
-			if (_keyboardStrokes.Count == 0) {
-				AddKeyboardStrokes (e);
-			} else {
-				_keyboardStrokes.Insert (0, 0);
-			}
-			if (_wasUnknown && (int)e.KeyCode - (int)(e.KeyCode & (KeyCode.AltMask | KeyCode.CtrlMask | KeyCode.ShiftMask)) != 0) {
-				unknownChar = e;
-			}
+
+			_keyboardStrokes.Add (e.KeyCode);
+		};
+
+		tvInput.KeyUp += (s, e) => {
+			//System.Diagnostics.Debug.WriteLine ($"Input - KeyUp: {e.Key}");
 			e.Handled = true;
 			e.Handled = true;
 			if (!_wasUnknown && _keyboardStrokes.Count > 0) {
 			if (!_wasUnknown && _keyboardStrokes.Count > 0) {
 				_outputStarted = true;
 				_outputStarted = true;
@@ -152,38 +153,31 @@ public class VkeyPacketSimulator : Scenario {
 				Task.Run (() => {
 				Task.Run (() => {
 					while (_outputStarted) {
 					while (_outputStarted) {
 						try {
 						try {
-							var mod = new ConsoleModifiers ();
-							if (e.KeyCode.HasFlag (KeyCode.ShiftMask)) {
-								mod |= ConsoleModifiers.Shift;
-							}
-							if (e.KeyCode.HasFlag (KeyCode.AltMask)) {
-								mod |= ConsoleModifiers.Alt;
-							}
-							if (e.KeyCode.HasFlag (KeyCode.CtrlMask)) {
-								mod |= ConsoleModifiers.Control;
-							}
-							for (int i = 0; i < _keyboardStrokes.Count; i++) {
-								var consoleKeyInfo = ConsoleKeyMapping.GetConsoleKeyFromKey ((uint)_keyboardStrokes [i], mod, out _);
-								Application.Driver.SendKeys (consoleKeyInfo.KeyChar, ConsoleKey.Packet, mod.HasFlag (ConsoleModifiers.Shift),
-									mod.HasFlag (ConsoleModifiers.Alt), mod.HasFlag (ConsoleModifiers.Control));
+							while (_keyboardStrokes.Count > 0) {
+								if (_keyboardStrokes [0] == KeyCode.Null) {
+									continue;
+								}
+								var consoleKeyInfo = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (_keyboardStrokes [0]);
+								var keyChar = ConsoleKeyMapping.EncodeKeyCharForVKPacket (consoleKeyInfo);
+								Application.Driver.SendKeys (keyChar, ConsoleKey.Packet, consoleKeyInfo.Modifiers.HasFlag (ConsoleModifiers.Shift),
+									consoleKeyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt), consoleKeyInfo.Modifiers.HasFlag (ConsoleModifiers.Control));
+
+								_stopOutput.Wait ();
+								_stopOutput.Reset ();
+								_keyboardStrokes.RemoveAt (0);
+								Application.Invoke (() => {
+									tvOutput.ReadOnly = true;
+									tvInput.SetFocus ();
+								});
 							}
 							}
-							//}
+							_outputStarted = false;
+
 						} catch (Exception) {
 						} catch (Exception) {
 							Application.Invoke (() => {
 							Application.Invoke (() => {
 								MessageBox.ErrorQuery ("Error", "Couldn't send the keystrokes!", "Ok");
 								MessageBox.ErrorQuery ("Error", "Couldn't send the keystrokes!", "Ok");
 								Application.RequestStop ();
 								Application.RequestStop ();
 							});
 							});
 						}
 						}
-						_stopOutput.Wait ();
-						_stopOutput.Reset ();
-						_keyboardStrokes.RemoveAt (0);
-						if (_keyboardStrokes.Count == 0) {
-							_outputStarted = false;
-							Application.Invoke (() => {
-								tvOutput.ReadOnly = true;
-								tvInput.SetFocus ();
-							});
-						}
 					}
 					}
 					//System.Diagnostics.Debug.WriteLine ($"_outputStarted: {_outputStarted}");
 					//System.Diagnostics.Debug.WriteLine ($"_outputStarted: {_outputStarted}");
 				});
 				});
@@ -214,12 +208,4 @@ public class VkeyPacketSimulator : Scenario {
 
 
 		Win.LayoutComplete += Win_LayoutComplete;
 		Win.LayoutComplete += Win_LayoutComplete;
 	}
 	}
-
-	void AddKeyboardStrokes (Key e)
-	{
-		int keyChar = (int)e.KeyCode;
-		int mK = (int)(e.KeyCode & (KeyCode.AltMask | KeyCode.CtrlMask | KeyCode.ShiftMask));
-		keyChar &= ~mK;
-		_keyboardStrokes.Add (keyChar);
-	}
-}
+}

+ 415 - 148
UnitTests/ConsoleDrivers/ConsoleKeyMappingTests.cs

@@ -1,46 +1,57 @@
 using System;
 using System;
-using System.Collections;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using Xunit;
 using Xunit;
+using static Terminal.Gui.ConsoleDrivers.ConsoleKeyMapping;
 
 
 namespace Terminal.Gui.ConsoleDrivers;
 namespace Terminal.Gui.ConsoleDrivers;
 public class ConsoleKeyMappingTests {
 public class ConsoleKeyMappingTests {
+
+#if ENABLE_VK_PACKET_NON_WINDOWS
+	// This test (and the GetConsoleKeyInfoFromKeyCode API) are bogus. They make no sense outside of
+	// the context of Windows and knowing they keyboard layout. They should be removed.
 	[Theory]
 	[Theory]
-	[InlineData ((KeyCode)'a' | KeyCode.ShiftMask, ConsoleKey.A, KeyCode.A, 'A')]
-	[InlineData ((KeyCode)'A', ConsoleKey.A, (KeyCode)'a', 'a')]
-	[InlineData ((KeyCode)'à' | KeyCode.ShiftMask, ConsoleKey.A, (KeyCode)'À', 'À')]
-	[InlineData ((KeyCode)'À', ConsoleKey.A, (KeyCode)'à', 'à')]
-	[InlineData ((KeyCode)'ü' | KeyCode.ShiftMask, ConsoleKey.U, (KeyCode)'Ü', 'Ü')]
-	[InlineData ((KeyCode)'Ü', ConsoleKey.U, (KeyCode)'ü', 'ü')]
-	[InlineData ((KeyCode)'ý' | KeyCode.ShiftMask, ConsoleKey.Y, (KeyCode)'Ý', 'Ý')]
-	[InlineData ((KeyCode)'Ý', ConsoleKey.Y, (KeyCode)'ý', 'ý')]
+	[InlineData (KeyCode.A | KeyCode.ShiftMask, ConsoleKey.A, KeyCode.A, 'A')]
+	[InlineData ((KeyCode)'a', ConsoleKey.A, (KeyCode)'a', 'a')]
+	[InlineData ((KeyCode)'À' | KeyCode.ShiftMask, ConsoleKey.A, (KeyCode)'À', 'À')]
+	[InlineData ((KeyCode)'à', ConsoleKey.A, (KeyCode)'à', 'à')]
+	[InlineData ((KeyCode)'Ü' | KeyCode.ShiftMask, ConsoleKey.U, (KeyCode)'Ü', 'Ü')]
+	[InlineData ((KeyCode)'ü', ConsoleKey.U, (KeyCode)'ü', 'ü')]
+	[InlineData ((KeyCode)'Ý' | KeyCode.ShiftMask, ConsoleKey.Y, (KeyCode)'Ý', 'Ý')]
+	[InlineData ((KeyCode)'ý', ConsoleKey.Y, (KeyCode)'ý', 'ý')]
 	[InlineData ((KeyCode)'!' | KeyCode.ShiftMask, ConsoleKey.D1, (KeyCode)'!', '!')]
 	[InlineData ((KeyCode)'!' | KeyCode.ShiftMask, ConsoleKey.D1, (KeyCode)'!', '!')]
+	[InlineData (KeyCode.D1 | KeyCode.ShiftMask, ConsoleKey.D1, (KeyCode)'!', '!')]
 	[InlineData (KeyCode.D1, ConsoleKey.D1, KeyCode.D1, '1')]
 	[InlineData (KeyCode.D1, ConsoleKey.D1, KeyCode.D1, '1')]
-	[InlineData ((KeyCode)'/' | KeyCode.ShiftMask, ConsoleKey.D7, (KeyCode)'/', '/')]
+	[InlineData ((KeyCode)'/' | KeyCode.ShiftMask, ConsoleKey.D7, (KeyCode)'/', '/')] // BUGBUG: This is incorrect for ENG keyboards. Shift-7 should be &.
+	[InlineData (KeyCode.D7 | KeyCode.ShiftMask, ConsoleKey.D7, (KeyCode)'/', '/')]
 	[InlineData (KeyCode.D7, ConsoleKey.D7, KeyCode.D7, '7')]
 	[InlineData (KeyCode.D7, ConsoleKey.D7, KeyCode.D7, '7')]
+	[InlineData ((KeyCode)'{' | KeyCode.AltMask | KeyCode.CtrlMask, ConsoleKey.D7, (KeyCode)'{', '{')]
+	[InlineData ((KeyCode)'?' | KeyCode.ShiftMask, ConsoleKey.Oem4, (KeyCode)'?', '?')]
+	[InlineData ((KeyCode)'\'', ConsoleKey.Oem4, (KeyCode)'\'', '\'')]
 	[InlineData (KeyCode.PageDown | KeyCode.ShiftMask, ConsoleKey.PageDown, KeyCode.Null, '\0')]
 	[InlineData (KeyCode.PageDown | KeyCode.ShiftMask, ConsoleKey.PageDown, KeyCode.Null, '\0')]
 	[InlineData (KeyCode.PageDown, ConsoleKey.PageDown, KeyCode.Null, '\0')]
 	[InlineData (KeyCode.PageDown, ConsoleKey.PageDown, KeyCode.Null, '\0')]
-
-	public void TestIfEqual (KeyCode key, ConsoleKey expectedConsoleKey, KeyCode expectedKey, char expectedChar)
+	[InlineData ((KeyCode)'q', ConsoleKey.Q, (KeyCode)'q', 'q')]
+	[InlineData (KeyCode.F2, ConsoleKey.F2, KeyCode.Null, '\0')]
+	[InlineData ((KeyCode)'英', ConsoleKey.None, (KeyCode)'英', '英')]
+	public void GetConsoleKeyInfoFromKeyCode_Tests (KeyCode keyCode, ConsoleKey expectedConsoleKey, KeyCode expectedKeyCode, char expectedKeyChar)
 	{
 	{
-		var consoleKeyInfo = ConsoleKeyMapping.GetConsoleKeyFromKey (key);
+		var consoleKeyInfo = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (keyCode);
 		Assert.Equal (consoleKeyInfo.Key, expectedConsoleKey);
 		Assert.Equal (consoleKeyInfo.Key, expectedConsoleKey);
-		Assert.Equal ((char)expectedKey, expectedChar);
-		Assert.Equal (consoleKeyInfo.KeyChar, expectedChar);
+		Assert.Equal ((char)expectedKeyCode, expectedKeyChar);
+		Assert.Equal (consoleKeyInfo.KeyChar, expectedKeyChar);
 	}
 	}
 
 
 	static object packetLock = new object ();
 	static object packetLock = new object ();
 
 
 	/// <summary>
 	/// <summary>
 	/// Sometimes when using remote tools EventKeyRecord sends 'virtual keystrokes'.
 	/// Sometimes when using remote tools EventKeyRecord sends 'virtual keystrokes'.
-	/// These are indicated with the wVirtualKeyCode of 231. When we see this code
+	/// These are indicated with the wVirtualKeyCode of 231 (VK_PACKET). When we see this code
 	/// then we need to look to the unicode character (UnicodeChar) instead of the key
 	/// then we need to look to the unicode character (UnicodeChar) instead of the key
 	/// when telling the rest of the framework what button was pressed. For full details
 	/// when telling the rest of the framework what button was pressed. For full details
 	/// see: https://github.com/gui-cs/Terminal.Gui/issues/2008
 	/// see: https://github.com/gui-cs/Terminal.Gui/issues/2008
 	/// </summary>
 	/// </summary>
 	[Theory]
 	[Theory]
 	[AutoInitShutdown]
 	[AutoInitShutdown]
-	[ClassData (typeof (PacketTest))]
+	[MemberData (nameof (VKPacket))]
 	public void TestVKPacket (uint unicodeCharacter, bool shift, bool alt, bool control, uint initialVirtualKey,
 	public void TestVKPacket (uint unicodeCharacter, bool shift, bool alt, bool control, uint initialVirtualKey,
 				uint initialScanCode, KeyCode expectedRemapping, uint expectedVirtualKey, uint expectedScanCode)
 				uint initialScanCode, KeyCode expectedRemapping, uint expectedVirtualKey, uint expectedScanCode)
 	{
 	{
@@ -48,20 +59,17 @@ public class ConsoleKeyMappingTests {
 			Application._forceFakeConsole = true;
 			Application._forceFakeConsole = true;
 			Application.Init ();
 			Application.Init ();
 
 
-			var modifiers = new ConsoleModifiers ();
-			if (shift) {
-				modifiers |= ConsoleModifiers.Shift;
-			}
-			if (alt) {
-				modifiers |= ConsoleModifiers.Alt;
-			}
-			if (control) {
-				modifiers |= ConsoleModifiers.Control;
-			}
-			ConsoleKeyInfo consoleKeyInfo = ConsoleKeyMapping.GetConsoleKeyFromKey (unicodeCharacter, modifiers, out uint scanCode);
+			ConsoleKeyInfo originalConsoleKeyInfo = new ConsoleKeyInfo ((char)unicodeCharacter, (ConsoleKey)initialVirtualKey, shift, alt, control);
+			var encodedChar = ConsoleKeyMapping.EncodeKeyCharForVKPacket (originalConsoleKeyInfo);
+			ConsoleKeyInfo packetConsoleKeyInfo = new ConsoleKeyInfo (encodedChar, ConsoleKey.Packet, shift, alt, control);
+			ConsoleKeyInfo consoleKeyInfo = ConsoleKeyMapping.DecodeVKPacketToKConsoleKeyInfo (packetConsoleKeyInfo);
 
 
-			Assert.Equal ((uint)consoleKeyInfo.Key, initialVirtualKey);
+			Assert.Equal (originalConsoleKeyInfo, consoleKeyInfo);
 
 
+			var modifiers = ConsoleKeyMapping.GetModifiers (shift, alt, control);
+			var scanCode = ConsoleKeyMapping.GetScanCodeFromConsoleKeyInfo (consoleKeyInfo);
+
+			Assert.Equal ((uint)consoleKeyInfo.Key, initialVirtualKey);
 
 
 			if (scanCode > 0 && consoleKeyInfo.KeyChar == 0) {
 			if (scanCode > 0 && consoleKeyInfo.KeyChar == 0) {
 				Assert.Equal (0, (double)consoleKeyInfo.KeyChar);
 				Assert.Equal (0, (double)consoleKeyInfo.KeyChar);
@@ -85,7 +93,8 @@ public class ConsoleKeyMappingTests {
 			Application.Iteration += (s, a) => {
 			Application.Iteration += (s, a) => {
 				iterations++;
 				iterations++;
 				if (iterations == 0) {
 				if (iterations == 0) {
-					Application.Driver.SendKeys (consoleKeyInfo.KeyChar, ConsoleKey.Packet, shift, alt, control);
+					var keyChar = ConsoleKeyMapping.EncodeKeyCharForVKPacket (consoleKeyInfo);
+					Application.Driver.SendKeys (keyChar, ConsoleKey.Packet, shift, alt, control);
 				}
 				}
 			};
 			};
 			Application.Run ();
 			Application.Run ();
@@ -93,126 +102,384 @@ public class ConsoleKeyMappingTests {
 		}
 		}
 	}
 	}
 
 
-	public class PacketTest : IEnumerable, IEnumerable<object []> {
-		public IEnumerator<object []> GetEnumerator ()
-		{
-			lock (packetLock) {
-				// unicodeCharacter, shift, alt, control, initialVirtualKey, initialScanCode, expectedRemapping, expectedVirtualKey, expectedScanCode
-				yield return new object [] { 'a', false, false, false, 'A', 30, KeyCode.A, 'A', 30 };
-				yield return new object [] { 'A', true, false, false, 'A', 30, KeyCode.A | KeyCode.ShiftMask, 'A', 30 };
-				yield return new object [] { 'A', true, true, false, 'A', 30, KeyCode.A | KeyCode.ShiftMask | KeyCode.AltMask, 'A', 30 };
-				yield return new object [] { 'A', true, true, true, 'A', 30, KeyCode.A | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, 'A', 30 };
-				yield return new object [] { 'z', false, false, false, 'Z', 44, KeyCode.Z, 'Z', 44 };
-				yield return new object [] { 'Z', true, false, false, 'Z', 44, KeyCode.Z | KeyCode.ShiftMask, 'Z', 44 };
-				yield return new object [] { 'Z', true, true, false, 'Z', 44, KeyCode.Z | KeyCode.ShiftMask | KeyCode.AltMask, 'Z', 44 };
-				yield return new object [] { 'Z', true, true, true, 'Z', 44, KeyCode.Z | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, 'Z', 44 };
-				yield return new object [] { '英', false, false, false, '\0', 0, (KeyCode)'英', '\0', 0 };
-				yield return new object [] { '英', true, false, false, '\0', 0, (KeyCode)'英' | KeyCode.ShiftMask, '\0', 0 };
-				yield return new object [] { '英', true, true, false, '\0', 0, (KeyCode)'英' | KeyCode.ShiftMask | KeyCode.AltMask, '\0', 0 };
-				yield return new object [] { '英', true, true, true, '\0', 0, (KeyCode)'英' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '\0', 0 };
-				yield return new object [] { '+', false, false, false, 187, 26, (KeyCode)'+', 187, 26 };
-				yield return new object [] { '*', true, false, false, 187, 26, (KeyCode)'*' | KeyCode.ShiftMask, 187, 26 };
-				yield return new object [] { '+', true, true, false, 187, 26, (KeyCode)'+' | KeyCode.ShiftMask | KeyCode.AltMask, 187, 26 };
-				yield return new object [] { '+', true, true, true, 187, 26, (KeyCode)'+' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, 187, 26 };
-				yield return new object [] { '1', false, false, false, '1', 2, KeyCode.D1, '1', 2 };
-				yield return new object [] { '!', true, false, false, '1', 2, (KeyCode)'!' | KeyCode.ShiftMask, '1', 2 };
-				yield return new object [] { '1', true, true, false, '1', 2, KeyCode.D1 | KeyCode.ShiftMask | KeyCode.AltMask, '1', 2 };
-				yield return new object [] { '1', true, true, true, '1', 2, KeyCode.D1 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '1', 2 };
-				yield return new object [] { '1', false, true, true, '1', 2, KeyCode.D1 | KeyCode.AltMask | KeyCode.CtrlMask, '1', 2 };
-				yield return new object [] { '2', false, false, false, '2', 3, KeyCode.D2, '2', 3 };
-				yield return new object [] { '"', true, false, false, '2', 3, (KeyCode)'"' | KeyCode.ShiftMask, '2', 3 };
-				yield return new object [] { '2', true, true, false, '2', 3, KeyCode.D2 | KeyCode.ShiftMask | KeyCode.AltMask, '2', 3 };
-				yield return new object [] { '2', true, true, true, '2', 3, KeyCode.D2 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '2', 3 };
-				yield return new object [] { '@', false, true, true, '2', 3, (KeyCode)'@' | KeyCode.AltMask | KeyCode.CtrlMask, '2', 3 };
-				yield return new object [] { '3', false, false, false, '3', 4, KeyCode.D3, '3', 4 };
-				yield return new object [] { '#', true, false, false, '3', 4, (KeyCode)'#' | KeyCode.ShiftMask, '3', 4 };
-				yield return new object [] { '3', true, true, false, '3', 4, KeyCode.D3 | KeyCode.ShiftMask | KeyCode.AltMask, '3', 4 };
-				yield return new object [] { '3', true, true, true, '3', 4, KeyCode.D3 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '3', 4 };
-				yield return new object [] { '£', false, true, true, '3', 4, (KeyCode)'£' | KeyCode.AltMask | KeyCode.CtrlMask, '3', 4 };
-				yield return new object [] { '4', false, false, false, '4', 5, KeyCode.D4, '4', 5 };
-				yield return new object [] { '$', true, false, false, '4', 5, (KeyCode)'$' | KeyCode.ShiftMask, '4', 5 };
-				yield return new object [] { '4', true, true, false, '4', 5, KeyCode.D4 | KeyCode.ShiftMask | KeyCode.AltMask, '4', 5 };
-				yield return new object [] { '4', true, true, true, '4', 5, KeyCode.D4 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '4', 5 };
-				yield return new object [] { '§', false, true, true, '4', 5, (KeyCode)'§' | KeyCode.AltMask | KeyCode.CtrlMask, '4', 5 };
-				yield return new object [] { '5', false, false, false, '5', 6, KeyCode.D5, '5', 6 };
-				yield return new object [] { '%', true, false, false, '5', 6, (KeyCode)'%' | KeyCode.ShiftMask, '5', 6 };
-				yield return new object [] { '5', true, true, false, '5', 6, KeyCode.D5 | KeyCode.ShiftMask | KeyCode.AltMask, '5', 6 };
-				yield return new object [] { '5', true, true, true, '5', 6, KeyCode.D5 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '5', 6 };
-				yield return new object [] { '€', false, true, true, '5', 6, (KeyCode)'€' | KeyCode.AltMask | KeyCode.CtrlMask, '5', 6 };
-				yield return new object [] { '6', false, false, false, '6', 7, KeyCode.D6, '6', 7 };
-				yield return new object [] { '&', true, false, false, '6', 7, (KeyCode)'&' | KeyCode.ShiftMask, '6', 7 };
-				yield return new object [] { '6', true, true, false, '6', 7, KeyCode.D6 | KeyCode.ShiftMask | KeyCode.AltMask, '6', 7 };
-				yield return new object [] { '6', true, true, true, '6', 7, KeyCode.D6 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '6', 7 };
-				yield return new object [] { '6', false, true, true, '6', 7, KeyCode.D6 | KeyCode.AltMask | KeyCode.CtrlMask, '6', 7 };
-				yield return new object [] { '7', false, false, false, '7', 8, KeyCode.D7, '7', 8 };
-				yield return new object [] { '/', true, false, false, '7', 8, (KeyCode)'/' | KeyCode.ShiftMask, '7', 8 };
-				yield return new object [] { '7', true, true, false, '7', 8, KeyCode.D7 | KeyCode.ShiftMask | KeyCode.AltMask, '7', 8 };
-				yield return new object [] { '7', true, true, true, '7', 8, KeyCode.D7 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '7', 8 };
-				yield return new object [] { '{', false, true, true, '7', 8, (KeyCode)'{' | KeyCode.AltMask | KeyCode.CtrlMask, '7', 8 };
-				yield return new object [] { '8', false, false, false, '8', 9, KeyCode.D8, '8', 9 };
-				yield return new object [] { '(', true, false, false, '8', 9, (KeyCode)'(' | KeyCode.ShiftMask, '8', 9 };
-				yield return new object [] { '8', true, true, false, '8', 9, KeyCode.D8 | KeyCode.ShiftMask | KeyCode.AltMask, '8', 9 };
-				yield return new object [] { '8', true, true, true, '8', 9, KeyCode.D8 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '8', 9 };
-				yield return new object [] { '[', false, true, true, '8', 9, (KeyCode)'[' | KeyCode.AltMask | KeyCode.CtrlMask, '8', 9 };
-				yield return new object [] { '9', false, false, false, '9', 10, KeyCode.D9, '9', 10 };
-				yield return new object [] { ')', true, false, false, '9', 10, (KeyCode)')' | KeyCode.ShiftMask, '9', 10 };
-				yield return new object [] { '9', true, true, false, '9', 10, KeyCode.D9 | KeyCode.ShiftMask | KeyCode.AltMask, '9', 10 };
-				yield return new object [] { '9', true, true, true, '9', 10, KeyCode.D9 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '9', 10 };
-				yield return new object [] { ']', false, true, true, '9', 10, (KeyCode)']' | KeyCode.AltMask | KeyCode.CtrlMask, '9', 10 };
-				yield return new object [] { '0', false, false, false, '0', 11, KeyCode.D0, '0', 11 };
-				yield return new object [] { '=', true, false, false, '0', 11, (KeyCode)'=' | KeyCode.ShiftMask, '0', 11 };
-				yield return new object [] { '0', true, true, false, '0', 11, KeyCode.D0 | KeyCode.ShiftMask | KeyCode.AltMask, '0', 11 };
-				yield return new object [] { '0', true, true, true, '0', 11, KeyCode.D0 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '0', 11 };
-				yield return new object [] { '}', false, true, true, '0', 11, (KeyCode)'}' | KeyCode.AltMask | KeyCode.CtrlMask, '0', 11 };
-				yield return new object [] { '\'', false, false, false, 219, 12, (KeyCode)'\'', 219, 12 };
-				yield return new object [] { '?', true, false, false, 219, 12, (KeyCode)'?' | KeyCode.ShiftMask, 219, 12 };
-				yield return new object [] { '\'', true, true, false, 219, 12, (KeyCode)'\'' | KeyCode.ShiftMask | KeyCode.AltMask, 219, 12 };
-				yield return new object [] { '\'', true, true, true, 219, 12, (KeyCode)'\'' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, 219, 12 };
-				yield return new object [] { '«', false, false, false, 221, 13, (KeyCode)'«', 221, 13 };
-				yield return new object [] { '»', true, false, false, 221, 13, (KeyCode)'»' | KeyCode.ShiftMask, 221, 13 };
-				yield return new object [] { '«', true, true, false, 221, 13, (KeyCode)'«' | KeyCode.ShiftMask | KeyCode.AltMask, 221, 13 };
-				yield return new object [] { '«', true, true, true, 221, 13, (KeyCode)'«' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, 221, 13 };
-				yield return new object [] { 'á', false, false, false, 'A', 30, (KeyCode)'á', 'A', 30 };
-				yield return new object [] { 'Á', true, false, false, 'A', 30, (KeyCode)'Á' | KeyCode.ShiftMask, 'A', 30 };
-				yield return new object [] { 'à', false, false, false, 'A', 30, (KeyCode)'à', 'A', 30 };
-				yield return new object [] { 'À', true, false, false, 'A', 30, (KeyCode)'À' | KeyCode.ShiftMask, 'A', 30 };
-				yield return new object [] { 'é', false, false, false, 'E', 18, (KeyCode)'é', 'E', 18 };
-				yield return new object [] { 'É', true, false, false, 'E', 18, (KeyCode)'É' | KeyCode.ShiftMask, 'E', 18 };
-				yield return new object [] { 'è', false, false, false, 'E', 18, (KeyCode)'è', 'E', 18 };
-				yield return new object [] { 'È', true, false, false, 'E', 18, (KeyCode)'È' | KeyCode.ShiftMask, 'E', 18 };
-				yield return new object [] { 'í', false, false, false, 'I', 23, (KeyCode)'í', 'I', 23 };
-				yield return new object [] { 'Í', true, false, false, 'I', 23, (KeyCode)'Í' | KeyCode.ShiftMask, 'I', 23 };
-				yield return new object [] { 'ì', false, false, false, 'I', 23, (KeyCode)'ì', 'I', 23 };
-				yield return new object [] { 'Ì', true, false, false, 'I', 23, (KeyCode)'Ì' | KeyCode.ShiftMask, 'I', 23 };
-				yield return new object [] { 'ó', false, false, false, 'O', 24, (KeyCode)'ó', 'O', 24 };
-				yield return new object [] { 'Ó', true, false, false, 'O', 24, (KeyCode)'Ó' | KeyCode.ShiftMask, 'O', 24 };
-				yield return new object [] { 'ò', false, false, false, 'O', 24, (KeyCode)'ò', 'O', 24 };
-				yield return new object [] { 'Ò', true, false, false, 'O', 24, (KeyCode)'Ò' | KeyCode.ShiftMask, 'O', 24 };
-				yield return new object [] { 'ú', false, false, false, 'U', 22, (KeyCode)'ú', 'U', 22 };
-				yield return new object [] { 'Ú', true, false, false, 'U', 22, (KeyCode)'Ú' | KeyCode.ShiftMask, 'U', 22 };
-				yield return new object [] { 'ù', false, false, false, 'U', 22, (KeyCode)'ù', 'U', 22 };
-				yield return new object [] { 'Ù', true, false, false, 'U', 22, (KeyCode)'Ù' | KeyCode.ShiftMask, 'U', 22 };
-				yield return new object [] { 'ö', false, false, false, 'O', 24, (KeyCode)'ö', 'O', 24 };
-				yield return new object [] { 'Ö', true, false, false, 'O', 24, (KeyCode)'Ö' | KeyCode.ShiftMask, 'O', 24 };
-				yield return new object [] { '<', false, false, false, 226, 86, (KeyCode)'<', 226, 86 };
-				yield return new object [] { '>', true, false, false, 226, 86, (KeyCode)'>' | KeyCode.ShiftMask, 226, 86 };
-				yield return new object [] { '<', true, true, false, 226, 86, (KeyCode)'<' | KeyCode.ShiftMask | KeyCode.AltMask, 226, 86 };
-				yield return new object [] { '<', true, true, true, 226, 86, (KeyCode)'<' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, 226, 86 };
-				yield return new object [] { 'ç', false, false, false, 192, 39, (KeyCode)'ç', 192, 39 };
-				yield return new object [] { 'Ç', true, false, false, 192, 39, (KeyCode)'Ç' | KeyCode.ShiftMask, 192, 39 };
-				yield return new object [] { 'ç', true, true, false, 192, 39, (KeyCode)'ç' | KeyCode.ShiftMask | KeyCode.AltMask, 192, 39 };
-				yield return new object [] { 'ç', true, true, true, 192, 39, (KeyCode)'ç' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, 192, 39 };
-				yield return new object [] { '¨', false, true, true, 187, 26, (KeyCode)'¨' | KeyCode.AltMask | KeyCode.CtrlMask, 187, 26 };
-				yield return new object [] { KeyCode.PageUp, false, false, false, 33, 73, KeyCode.Null, 33, 73 };
-				yield return new object [] { KeyCode.PageUp, true, false, false, 33, 73, KeyCode.Null | KeyCode.ShiftMask, 33, 73 };
-				yield return new object [] { KeyCode.PageUp, true, true, false, 33, 73, KeyCode.Null | KeyCode.ShiftMask | KeyCode.AltMask, 33, 73 };
-				yield return new object [] { KeyCode.PageUp, true, true, true, 33, 73, KeyCode.Null | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, 33, 73 };
-			}
+	public static IEnumerable<object []> VKPacket ()
+	{
+		lock (packetLock) {
+			// unicodeCharacter, shift, alt, control, initialVirtualKey, initialScanCode, expectedRemapping, expectedVirtualKey, expectedScanCode
+			yield return new object [] { 'a', false, false, false, 'A', 30, KeyCode.A, 'A', 30 };
+			yield return new object [] { 'A', true, false, false, 'A', 30, KeyCode.A | KeyCode.ShiftMask, 'A', 30 };
+			yield return new object [] { 'A', true, true, false, 'A', 30, KeyCode.A | KeyCode.ShiftMask | KeyCode.AltMask, 'A', 30 };
+			yield return new object [] { 'A', true, true, true, 'A', 30, KeyCode.A | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, 'A', 30 };
+			yield return new object [] { 'z', false, false, false, 'Z', 44, KeyCode.Z, 'Z', 44 };
+			yield return new object [] { 'Z', true, false, false, 'Z', 44, KeyCode.Z | KeyCode.ShiftMask, 'Z', 44 };
+			yield return new object [] { 'Z', true, true, false, 'Z', 44, KeyCode.Z | KeyCode.ShiftMask | KeyCode.AltMask, 'Z', 44 };
+			yield return new object [] { 'Z', true, true, true, 'Z', 44, KeyCode.Z | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, 'Z', 44 };
+			yield return new object [] { '英', false, false, false, '\0', 0, (KeyCode)'英', '\0', 0 };
+			yield return new object [] { '英', true, false, false, '\0', 0, (KeyCode)'英' | KeyCode.ShiftMask, '\0', 0 };
+			yield return new object [] { '英', true, true, false, '\0', 0, (KeyCode)'英' | KeyCode.ShiftMask | KeyCode.AltMask, '\0', 0 };
+			yield return new object [] { '英', true, true, true, '\0', 0, (KeyCode)'英' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '\0', 0 };
+			yield return new object [] { '+', false, false, false, VK.OEM_PLUS, 26, (KeyCode)'+', VK.OEM_PLUS, 26 };
+			yield return new object [] { '*', true, false, false, VK.OEM_PLUS, 26, (KeyCode)'*' | KeyCode.ShiftMask, VK.OEM_PLUS, 26 };
+			yield return new object [] { '+', true, true, false, VK.OEM_PLUS, 26, (KeyCode)'+' | KeyCode.ShiftMask | KeyCode.AltMask, VK.OEM_PLUS, 26 };
+			yield return new object [] { '+', true, true, true, VK.OEM_PLUS, 26, (KeyCode)'+' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, VK.OEM_PLUS, 26 };
+			yield return new object [] { '1', false, false, false, '1', 2, KeyCode.D1, '1', 2 };
+			yield return new object [] { '!', true, false, false, '1', 2, (KeyCode)'!' | KeyCode.ShiftMask, '1', 2 };
+			yield return new object [] { '1', true, true, false, '1', 2, KeyCode.D1 | KeyCode.ShiftMask | KeyCode.AltMask, '1', 2 };
+			yield return new object [] { '1', true, true, true, '1', 2, KeyCode.D1 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '1', 2 };
+			yield return new object [] { '1', false, true, true, '1', 2, KeyCode.D1 | KeyCode.AltMask | KeyCode.CtrlMask, '1', 2 };
+			yield return new object [] { '2', false, false, false, '2', 3, KeyCode.D2, '2', 3 };
+			yield return new object [] { '"', true, false, false, '2', 3, (KeyCode)'"' | KeyCode.ShiftMask, '2', 3 };
+			yield return new object [] { '2', true, true, false, '2', 3, KeyCode.D2 | KeyCode.ShiftMask | KeyCode.AltMask, '2', 3 };
+			yield return new object [] { '2', true, true, true, '2', 3, KeyCode.D2 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '2', 3 };
+			yield return new object [] { '@', false, true, true, '2', 3, (KeyCode)'@' | KeyCode.AltMask | KeyCode.CtrlMask, '2', 3 };
+			yield return new object [] { '3', false, false, false, '3', 4, KeyCode.D3, '3', 4 };
+			yield return new object [] { '#', true, false, false, '3', 4, (KeyCode)'#' | KeyCode.ShiftMask, '3', 4 };
+			yield return new object [] { '3', true, true, false, '3', 4, KeyCode.D3 | KeyCode.ShiftMask | KeyCode.AltMask, '3', 4 };
+			yield return new object [] { '3', true, true, true, '3', 4, KeyCode.D3 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '3', 4 };
+			yield return new object [] { '£', false, true, true, '3', 4, (KeyCode)'£' | KeyCode.AltMask | KeyCode.CtrlMask, '3', 4 };
+			yield return new object [] { '4', false, false, false, '4', 5, KeyCode.D4, '4', 5 };
+			yield return new object [] { '$', true, false, false, '4', 5, (KeyCode)'$' | KeyCode.ShiftMask, '4', 5 };
+			yield return new object [] { '4', true, true, false, '4', 5, KeyCode.D4 | KeyCode.ShiftMask | KeyCode.AltMask, '4', 5 };
+			yield return new object [] { '4', true, true, true, '4', 5, KeyCode.D4 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '4', 5 };
+			yield return new object [] { '§', false, true, true, '4', 5, (KeyCode)'§' | KeyCode.AltMask | KeyCode.CtrlMask, '4', 5 };
+			yield return new object [] { '5', false, false, false, '5', 6, KeyCode.D5, '5', 6 };
+			yield return new object [] { '%', true, false, false, '5', 6, (KeyCode)'%' | KeyCode.ShiftMask, '5', 6 };
+			yield return new object [] { '5', true, true, false, '5', 6, KeyCode.D5 | KeyCode.ShiftMask | KeyCode.AltMask, '5', 6 };
+			yield return new object [] { '5', true, true, true, '5', 6, KeyCode.D5 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '5', 6 };
+			yield return new object [] { '€', false, true, true, '5', 6, (KeyCode)'€' | KeyCode.AltMask | KeyCode.CtrlMask, '5', 6 };
+			yield return new object [] { '6', false, false, false, '6', 7, KeyCode.D6, '6', 7 };
+			yield return new object [] { '&', true, false, false, '6', 7, (KeyCode)'&' | KeyCode.ShiftMask, '6', 7 };
+			yield return new object [] { '6', true, true, false, '6', 7, KeyCode.D6 | KeyCode.ShiftMask | KeyCode.AltMask, '6', 7 };
+			yield return new object [] { '6', true, true, true, '6', 7, KeyCode.D6 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '6', 7 };
+			yield return new object [] { '6', false, true, true, '6', 7, KeyCode.D6 | KeyCode.AltMask | KeyCode.CtrlMask, '6', 7 };
+			yield return new object [] { '7', false, false, false, '7', 8, KeyCode.D7, '7', 8 };
+			yield return new object [] { '/', true, false, false, '7', 8, (KeyCode)'/' | KeyCode.ShiftMask, '7', 8 }; // BUGBUG: This is not true for ENG keyboards. Shift-7 is &.
+			yield return new object [] { '7', true, true, false, '7', 8, KeyCode.D7 | KeyCode.ShiftMask | KeyCode.AltMask, '7', 8 };
+			yield return new object [] { '7', true, true, true, '7', 8, KeyCode.D7 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '7', 8 };
+			yield return new object [] { '{', false, true, true, '7', 8, (KeyCode)'{' | KeyCode.AltMask | KeyCode.CtrlMask, '7', 8 };
+			yield return new object [] { '8', false, false, false, '8', 9, KeyCode.D8, '8', 9 };
+			yield return new object [] { '(', true, false, false, '8', 9, (KeyCode)'(' | KeyCode.ShiftMask, '8', 9 };
+			yield return new object [] { '8', true, true, false, '8', 9, KeyCode.D8 | KeyCode.ShiftMask | KeyCode.AltMask, '8', 9 };
+			yield return new object [] { '8', true, true, true, '8', 9, KeyCode.D8 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '8', 9 };
+			yield return new object [] { '[', false, true, true, '8', 9, (KeyCode)'[' | KeyCode.AltMask | KeyCode.CtrlMask, '8', 9 };
+			yield return new object [] { '9', false, false, false, '9', 10, KeyCode.D9, '9', 10 };
+			yield return new object [] { ')', true, false, false, '9', 10, (KeyCode)')' | KeyCode.ShiftMask, '9', 10 };
+			yield return new object [] { '9', true, true, false, '9', 10, KeyCode.D9 | KeyCode.ShiftMask | KeyCode.AltMask, '9', 10 };
+			yield return new object [] { '9', true, true, true, '9', 10, KeyCode.D9 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '9', 10 };
+			yield return new object [] { ']', false, true, true, '9', 10, (KeyCode)']' | KeyCode.AltMask | KeyCode.CtrlMask, '9', 10 };
+			yield return new object [] { '0', false, false, false, '0', 11, KeyCode.D0, '0', 11 };
+			yield return new object [] { '=', true, false, false, '0', 11, (KeyCode)'=' | KeyCode.ShiftMask, '0', 11 };
+			yield return new object [] { '0', true, true, false, '0', 11, KeyCode.D0 | KeyCode.ShiftMask | KeyCode.AltMask, '0', 11 };
+			yield return new object [] { '0', true, true, true, '0', 11, KeyCode.D0 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '0', 11 };
+			yield return new object [] { '}', false, true, true, '0', 11, (KeyCode)'}' | KeyCode.AltMask | KeyCode.CtrlMask, '0', 11 };
+			yield return new object [] { '\'', false, false, false, VK.OEM_4, 12, (KeyCode)'\'', VK.OEM_4, 12 };
+			yield return new object [] { '?', true, false, false, VK.OEM_4, 12, (KeyCode)'?' | KeyCode.ShiftMask, VK.OEM_4, 12 };
+			yield return new object [] { '\'', true, true, false, VK.OEM_4, 12, (KeyCode)'\'' | KeyCode.ShiftMask | KeyCode.AltMask, VK.OEM_4, 12 };
+			yield return new object [] { '\'', true, true, true, VK.OEM_4, 12, (KeyCode)'\'' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, VK.OEM_4, 12 };
+			yield return new object [] { '«', false, false, false, VK.OEM_6, 13, (KeyCode)'«', VK.OEM_6, 13 };
+			yield return new object [] { '»', true, false, false, VK.OEM_6, 13, (KeyCode)'»' | KeyCode.ShiftMask, VK.OEM_6, 13 };
+			yield return new object [] { '«', true, true, false, VK.OEM_6, 13, (KeyCode)'«' | KeyCode.ShiftMask | KeyCode.AltMask, VK.OEM_6, 13 };
+			yield return new object [] { '«', true, true, true, VK.OEM_6, 13, (KeyCode)'«' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, VK.OEM_6, 13 };
+			yield return new object [] { 'á', false, false, false, 'A', 30, (KeyCode)'á', 'A', 30 };
+			yield return new object [] { 'Á', true, false, false, 'A', 30, (KeyCode)'Á' | KeyCode.ShiftMask, 'A', 30 };
+			yield return new object [] { 'à', false, false, false, 'A', 30, (KeyCode)'à', 'A', 30 };
+			yield return new object [] { 'À', true, false, false, 'A', 30, (KeyCode)'À' | KeyCode.ShiftMask, 'A', 30 };
+			yield return new object [] { 'é', false, false, false, 'E', 18, (KeyCode)'é', 'E', 18 };
+			yield return new object [] { 'É', true, false, false, 'E', 18, (KeyCode)'É' | KeyCode.ShiftMask, 'E', 18 };
+			yield return new object [] { 'è', false, false, false, 'E', 18, (KeyCode)'è', 'E', 18 };
+			yield return new object [] { 'È', true, false, false, 'E', 18, (KeyCode)'È' | KeyCode.ShiftMask, 'E', 18 };
+			yield return new object [] { 'í', false, false, false, 'I', 23, (KeyCode)'í', 'I', 23 };
+			yield return new object [] { 'Í', true, false, false, 'I', 23, (KeyCode)'Í' | KeyCode.ShiftMask, 'I', 23 };
+			yield return new object [] { 'ì', false, false, false, 'I', 23, (KeyCode)'ì', 'I', 23 };
+			yield return new object [] { 'Ì', true, false, false, 'I', 23, (KeyCode)'Ì' | KeyCode.ShiftMask, 'I', 23 };
+			yield return new object [] { 'ó', false, false, false, 'O', 24, (KeyCode)'ó', 'O', 24 };
+			yield return new object [] { 'Ó', true, false, false, 'O', 24, (KeyCode)'Ó' | KeyCode.ShiftMask, 'O', 24 };
+			yield return new object [] { 'ò', false, false, false, 'O', 24, (KeyCode)'ò', 'O', 24 };
+			yield return new object [] { 'Ò', true, false, false, 'O', 24, (KeyCode)'Ò' | KeyCode.ShiftMask, 'O', 24 };
+			yield return new object [] { 'ú', false, false, false, 'U', 22, (KeyCode)'ú', 'U', 22 };
+			yield return new object [] { 'Ú', true, false, false, 'U', 22, (KeyCode)'Ú' | KeyCode.ShiftMask, 'U', 22 };
+			yield return new object [] { 'ù', false, false, false, 'U', 22, (KeyCode)'ù', 'U', 22 };
+			yield return new object [] { 'Ù', true, false, false, 'U', 22, (KeyCode)'Ù' | KeyCode.ShiftMask, 'U', 22 };
+			yield return new object [] { 'ö', false, false, false, 'O', 24, (KeyCode)'ö', 'O', 24 };
+			yield return new object [] { 'Ö', true, false, false, 'O', 24, (KeyCode)'Ö' | KeyCode.ShiftMask, 'O', 24 };
+			yield return new object [] { '<', false, false, false, VK.OEM_102, 86, (KeyCode)'<', VK.OEM_102, 86 };
+			yield return new object [] { '>', true, false, false, VK.OEM_102, 86, (KeyCode)'>' | KeyCode.ShiftMask, VK.OEM_102, 86 };
+			yield return new object [] { '<', true, true, false, VK.OEM_102, 86, (KeyCode)'<' | KeyCode.ShiftMask | KeyCode.AltMask, VK.OEM_102, 86 };
+			yield return new object [] { '<', true, true, true, VK.OEM_102, 86, (KeyCode)'<' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, VK.OEM_102, 86 };
+			yield return new object [] { 'ç', false, false, false, VK.OEM_3, 39, (KeyCode)'ç', VK.OEM_3, 39 };
+			yield return new object [] { 'Ç', true, false, false, VK.OEM_3, 39, (KeyCode)'Ç' | KeyCode.ShiftMask, VK.OEM_3, 39 };
+			yield return new object [] { 'ç', true, true, false, VK.OEM_3, 39, (KeyCode)'ç' | KeyCode.ShiftMask | KeyCode.AltMask, VK.OEM_3, 39 };
+			yield return new object [] { 'ç', true, true, true, VK.OEM_3, 39, (KeyCode)'ç' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, VK.OEM_3, 39 };
+			yield return new object [] { '¨', false, true, true, VK.OEM_PLUS, 26, (KeyCode)'¨' | KeyCode.AltMask | KeyCode.CtrlMask, VK.OEM_PLUS, 26 };
+			yield return new object [] { '\0', false, false, false, VK.PRIOR, 73, KeyCode.PageUp, VK.PRIOR, 73 };
+			yield return new object [] { '\0', true, false, false, VK.PRIOR, 73, KeyCode.PageUp | KeyCode.ShiftMask, VK.PRIOR, 73 };
+			yield return new object [] { '\0', true, true, false, VK.PRIOR, 73, KeyCode.PageUp | KeyCode.ShiftMask | KeyCode.AltMask, VK.PRIOR, 73 };
+			yield return new object [] { '\0', true, true, true, VK.PRIOR, 73, KeyCode.PageUp | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, VK.PRIOR, 73 };
+			yield return new object [] { '~', false, false, false, VK.SPACE, 57, (KeyCode)'~', VK.SPACE, 57 };
+			yield return new object [] { '^', false, false, false, VK.SPACE, 57, (KeyCode)'^', VK.SPACE, 57 };
 		}
 		}
+	}
+
+	[Theory]
+	[InlineData ('a', ConsoleKey.A, 'a', ConsoleKey.A)]
+	[InlineData ('A', ConsoleKey.A, 'A', ConsoleKey.A)]
+	[InlineData ('á', ConsoleKey.A, 'á', ConsoleKey.A)]
+	[InlineData ('Á', ConsoleKey.A, 'Á', ConsoleKey.A)]
+	[InlineData ('à', ConsoleKey.A, 'à', ConsoleKey.A)]
+	[InlineData ('À', ConsoleKey.A, 'À', ConsoleKey.A)]
+	[InlineData ('5', ConsoleKey.D5, '5', ConsoleKey.D5)]
+	[InlineData ('%', ConsoleKey.D5, '%', ConsoleKey.D5)]
+	[InlineData ('€', ConsoleKey.D5, '€', ConsoleKey.D5)]
+	[InlineData ('?', ConsoleKey.Oem4, '?', ConsoleKey.Oem4)]
+	[InlineData ('\'', ConsoleKey.Oem4, '\'', ConsoleKey.Oem4)]
+	[InlineData ('q', ConsoleKey.Q, 'q', ConsoleKey.Q)]
+	[InlineData ('\0', ConsoleKey.F2, '\0', ConsoleKey.F2)]
+	[InlineData ('英', ConsoleKey.None, '英', ConsoleKey.None)]
+	[InlineData ('´', ConsoleKey.None, '´', ConsoleKey.Oem1)]
+	[InlineData ('`', ConsoleKey.None, '`', ConsoleKey.Oem1)]
+	//[InlineData ('~', ConsoleKey.None, '~', ConsoleKey.Oem2)]
+	//[InlineData ('^', ConsoleKey.None, '^', ConsoleKey.Oem2)] // BUGBUG: '^' is Shift-6 on ENG keyboard,not Oem2.
+                                                                  // For the US standard keyboard, Oem2 is the /? key
+	public void EncodeKeyCharForVKPacket_DecodeVKPacketToKConsoleKeyInfo (char keyChar, ConsoleKey consoleKey, char expectedChar, ConsoleKey expectedConsoleKey)
+	{
+		var consoleKeyInfo = new ConsoleKeyInfo (keyChar, consoleKey, false, false, false);
+		var encodedKeyChar = ConsoleKeyMapping.EncodeKeyCharForVKPacket (consoleKeyInfo);
+		var encodedConsoleKeyInfo = new ConsoleKeyInfo (encodedKeyChar, ConsoleKey.Packet, false, false, false);
+		var decodedConsoleKeyInfo = ConsoleKeyMapping.DecodeVKPacketToKConsoleKeyInfo (encodedConsoleKeyInfo);
+		Assert.Equal (consoleKeyInfo.Key, consoleKey);
+		Assert.Equal (expectedConsoleKey, decodedConsoleKeyInfo.Key);
+		Assert.Equal (expectedChar, decodedConsoleKeyInfo.KeyChar);
+	}
 
 
-		IEnumerator IEnumerable.GetEnumerator ()
-		{
-			return GetEnumerator ();
+	
+	[Theory]
+	[InlineData ((KeyCode)'a', false, ConsoleKey.A, 'a')]
+	[InlineData (KeyCode.A | KeyCode.ShiftMask, false, ConsoleKey.A, 'A')]
+	[InlineData ((KeyCode)'á', false, ConsoleKey.A, 'á')]
+	[InlineData ((KeyCode)'Á' | KeyCode.ShiftMask, false, ConsoleKey.A, 'Á')]
+	[InlineData ((KeyCode)'à', false, ConsoleKey.A, 'à')]
+	[InlineData ((KeyCode)'À' | KeyCode.ShiftMask, false, ConsoleKey.A, 'À')]
+	[InlineData (KeyCode.D5, false, ConsoleKey.D5, '5')]
+	[InlineData ((KeyCode)'%' | KeyCode.ShiftMask, false, ConsoleKey.D5, '%')]
+	//[InlineData ((KeyCode)'€' | KeyCode.AltMask | KeyCode.CtrlMask, false, ConsoleKey.D5, '€')] // Bogus test. This is not true on ENG keyboard layout.
+	[InlineData ((KeyCode)'?' | KeyCode.ShiftMask, false, ConsoleKey.Oem4, '?')]
+	[InlineData ((KeyCode)'\'', false, ConsoleKey.Oem4, '\'')]
+	[InlineData ((KeyCode)'q', false, ConsoleKey.Q, 'q')]
+	[InlineData (KeyCode.F2, true, ConsoleKey.F2, 'q')]
+	[InlineData ((KeyCode)'英', false, ConsoleKey.None, '英')]
+	[InlineData (KeyCode.Enter, true, ConsoleKey.Enter, '\r')]
+	public void MapKeyCodeToConsoleKey_GetKeyCharFromUnicodeChar (KeyCode keyCode, bool expectedIsConsoleKey, ConsoleKey expectedConsoleKey, char expectedConsoleKeyChar)
+	{
+		var modifiers = ConsoleKeyMapping.MapToConsoleModifiers (keyCode);
+		var consoleKey = ConsoleKeyMapping.MapKeyCodeToConsoleKey (keyCode, out bool isConsoleKey);
+		if (isConsoleKey) {
+			Assert.True (isConsoleKey == expectedIsConsoleKey);
+			Assert.Equal (expectedConsoleKey , (ConsoleKey)consoleKey);
+			Assert.Equal (expectedConsoleKeyChar, consoleKey);
+		} else {
+			var keyChar = ConsoleKeyMapping.GetKeyCharFromUnicodeChar (consoleKey, modifiers, out consoleKey, out _, isConsoleKey);
+			Assert.True (isConsoleKey == expectedIsConsoleKey);
+			Assert.Equal (expectedConsoleKey , (ConsoleKey)consoleKey);
+			Assert.Equal (expectedConsoleKeyChar, keyChar);
 		}
 		}
 	}
 	}
+#endif
+	
+	[Theory]
+	[InlineData ('a', ConsoleKey.A, false, false, false, (KeyCode)'a')]
+	[InlineData ('A', ConsoleKey.A, true, false, false, KeyCode.A | KeyCode.ShiftMask)]
+	[InlineData ('á', ConsoleKey.A, false, false, false, (KeyCode)'á')]
+	[InlineData ('Á', ConsoleKey.A, true, false, false, (KeyCode)'Á' | KeyCode.ShiftMask)]
+	[InlineData ('à', ConsoleKey.A, false, false, false, (KeyCode)'à')]
+	[InlineData ('À', ConsoleKey.A, true, false, false, (KeyCode)'À' | KeyCode.ShiftMask)]
+	[InlineData ('5', ConsoleKey.D5, false, false, false, KeyCode.D5)]
+	[InlineData ('%', ConsoleKey.D5, true, false, false, (KeyCode)'%' | KeyCode.ShiftMask)]
+	[InlineData ('€', ConsoleKey.D5, false, true, true, (KeyCode)'€' | KeyCode.AltMask | KeyCode.CtrlMask)]
+	[InlineData ('?', ConsoleKey.Oem4, true, false, false, (KeyCode)'?' | KeyCode.ShiftMask)]
+	[InlineData ('\'', ConsoleKey.Oem4, false, false, false, (KeyCode)'\'')]
+	[InlineData ('q', ConsoleKey.Q, false, false, false, (KeyCode)'q')]
+	[InlineData ('\0', ConsoleKey.F2, false, false, false, KeyCode.F2)]
+	[InlineData ('英', ConsoleKey.None, false, false, false, (KeyCode)'英')]
+	[InlineData ('\r', ConsoleKey.Enter, false, false, false, KeyCode.Enter)]
+	public void MapConsoleKeyInfoToKeyCode_Also_Return_Modifiers (char keyChar, ConsoleKey consoleKey, bool shift, bool alt, bool control, KeyCode expectedKeyCode)
+	{
+		var consoleKeyInfo = new ConsoleKeyInfo (keyChar, consoleKey, shift, alt, control);
+		var keyCode = ConsoleKeyMapping.MapConsoleKeyInfoToKeyCode (consoleKeyInfo);
+
+		Assert.Equal (keyCode, expectedKeyCode);
+	}
+
+	[Theory]
+	[InlineData ('a', false, false, false, (KeyCode)'a')]
+	[InlineData ('A', true, false, false, KeyCode.A | KeyCode.ShiftMask)]
+	[InlineData ('á', false, false, false, (KeyCode)'á')]
+	[InlineData ('Á', true, false, false, (KeyCode)'Á' | KeyCode.ShiftMask)]
+	[InlineData ('à', false, false, false, (KeyCode)'à')]
+	[InlineData ('À', true, false, false, (KeyCode)'À' | KeyCode.ShiftMask)]
+	[InlineData ('5', false, false, false, KeyCode.D5)]
+	[InlineData ('%', true, false, false, (KeyCode)'%' | KeyCode.ShiftMask)]
+	[InlineData ('€', false, true, true, (KeyCode)'€' | KeyCode.AltMask | KeyCode.CtrlMask)]
+	[InlineData ('?', true, false, false, (KeyCode)'?' | KeyCode.ShiftMask)]
+	[InlineData ('\'', false, false, false, (KeyCode)'\'')]
+	[InlineData ('q', false, false, false, (KeyCode)'q')]
+	[InlineData ((uint)KeyCode.F2, false, false, false, KeyCode.F2)]
+	[InlineData ('英', false, false, false, (KeyCode)'英')]
+	[InlineData ('\r', false, false, false, KeyCode.Enter)]
+	[InlineData ('\n', false, false, false, (KeyCode)'\n')]
+	public void MapToKeyCodeModifiers_Tests (uint keyChar, bool shift, bool alt, bool control, KeyCode excpectedKeyCode)
+	{
+		var modifiers = ConsoleKeyMapping.GetModifiers (shift, alt, control);
+		KeyCode keyCode = (KeyCode)keyChar;
+		keyCode = ConsoleKeyMapping.MapToKeyCodeModifiers (modifiers, keyCode);
+
+		Assert.Equal (keyCode, excpectedKeyCode);
+	}
+
+	[Theory]
+	[InlineData ('a', ConsoleKey.A, false, false, false, 30)]
+	[InlineData ('A', ConsoleKey.A, true, false, false, 30)]
+	[InlineData ('á', ConsoleKey.A, false, false, false, 30)]
+	[InlineData ('Á', ConsoleKey.A, true, false, false, 30)]
+	[InlineData ('à', ConsoleKey.A, false, false, false, 30)]
+	[InlineData ('À', ConsoleKey.A, true, false, false, 30)]
+	[InlineData ('0', ConsoleKey.D0, false, false, false, 11)]
+	[InlineData ('=', ConsoleKey.D0, true, false, false, 11)]
+	[InlineData ('}', ConsoleKey.D0, false, true, true, 11)]
+	[InlineData ('1', ConsoleKey.D1, false, false, false, 2)]
+	[InlineData ('!', ConsoleKey.D1, true, false, false, 2)]
+	[InlineData ('2', ConsoleKey.D2, false, false, false, 3)]
+	[InlineData ('"', ConsoleKey.D2, true, false, false, 3)]
+	[InlineData ('@', ConsoleKey.D2, false, true, true, 3)]
+	[InlineData ('3', ConsoleKey.D3, false, false, false, 4)]
+	[InlineData ('#', ConsoleKey.D3, true, false, false, 4)]
+	[InlineData ('£', ConsoleKey.D3, false, true, true, 4)]
+	[InlineData ('4', ConsoleKey.D4, false, false, false, 5)]
+	[InlineData ('$', ConsoleKey.D4, true, false, false, 5)]
+	[InlineData ('§', ConsoleKey.D4, false, true, true, 5)]
+	[InlineData ('5', ConsoleKey.D5, false, false, false, 6)]
+	[InlineData ('%', ConsoleKey.D5, true, false, false, 6)]
+	[InlineData ('€', ConsoleKey.D5, false, true, true, 6)]
+	[InlineData ('6', ConsoleKey.D6, false, false, false, 7)]
+	[InlineData ('&', ConsoleKey.D6, true, false, false, 7)]
+	[InlineData ('7', ConsoleKey.D7, false, false, false, 8)]
+	[InlineData ('/', ConsoleKey.D7, true, false, false, 8)]
+	[InlineData ('{', ConsoleKey.D7, false, true, true, 8)]
+	[InlineData ('8', ConsoleKey.D8, false, false, false, 9)]
+	[InlineData ('(', ConsoleKey.D8, true, false, false, 9)]
+	[InlineData ('[', ConsoleKey.D8, false, true, true, 9)]
+	[InlineData ('9', ConsoleKey.D9, false, false, false, 10)]
+	[InlineData (')', ConsoleKey.D9, true, false, false, 10)]
+	[InlineData (']', ConsoleKey.D9, false, true, true, 10)]
+	[InlineData ('´', ConsoleKey.Oem1, false, false, false, 27)]
+	[InlineData ('`', ConsoleKey.Oem1, true, false, false, 27)]
+	[InlineData ('~', ConsoleKey.Oem2, false, false, false, 43)]
+	[InlineData ('^', ConsoleKey.Oem2, true, false, false, 43)]
+	[InlineData ('ç', ConsoleKey.Oem3, false, false, false, 39)]
+	[InlineData ('Ç', ConsoleKey.Oem3, true, false, false, 39)]
+	[InlineData ('\'', ConsoleKey.Oem4, false, false, false, 12)]
+	[InlineData ('?', ConsoleKey.Oem4, true, false, false, 12)]
+	[InlineData ('\\', ConsoleKey.Oem5, false, true, true, 41)]
+	[InlineData ('|', ConsoleKey.Oem5, true, false, false, 41)]
+	[InlineData ('«', ConsoleKey.Oem6, false, true, true, 13)]
+	[InlineData ('»', ConsoleKey.Oem6, true, false, false, 13)]
+	[InlineData ('º', ConsoleKey.Oem7, false, true, true, 40)]
+	[InlineData ('ª', ConsoleKey.Oem7, true, false, false, 40)]
+	[InlineData ('+', ConsoleKey.OemPlus, false, true, true, 26)]
+	[InlineData ('*', ConsoleKey.OemPlus, true, false, false, 26)]
+	[InlineData ('¨', ConsoleKey.OemPlus, false, true, true, 26)]
+	[InlineData (',', ConsoleKey.OemComma, false, true, true, 51)]
+	[InlineData (';', ConsoleKey.OemComma, true, false, false, 51)]
+	[InlineData ('.', ConsoleKey.OemPeriod, false, true, true, 52)]
+	[InlineData (':', ConsoleKey.OemPeriod, true, false, false, 52)]
+	[InlineData ('-', ConsoleKey.OemMinus, false, true, true, 53)]
+	[InlineData ('_', ConsoleKey.OemMinus, true, false, false, 53)]
+	[InlineData ('q', ConsoleKey.Q, false, false, false, 16)]
+	[InlineData ('\0', ConsoleKey.F2, false, false, false, 60)]
+	[InlineData ('英', ConsoleKey.None, false, false, false, 0)]
+	[InlineData ('英', ConsoleKey.None, true, false, false, 0)]
+	public void GetScanCodeFromConsoleKeyInfo_Tests (char keyChar, ConsoleKey consoleKey, bool shift, bool alt, bool control, uint expectedScanCode)
+	{
+		var consoleKeyInfo = new ConsoleKeyInfo (keyChar, consoleKey, shift, alt, control);
+		var scanCode = ConsoleKeyMapping.GetScanCodeFromConsoleKeyInfo (consoleKeyInfo);
+
+		Assert.Equal (scanCode, expectedScanCode);
+	}
+
+	[Theory]
+	[InlineData ('a', 'A', KeyCode.A | KeyCode.ShiftMask)]
+	[InlineData ('z', 'Z', KeyCode.Z | KeyCode.ShiftMask)]
+	[InlineData ('á', 'Á', (KeyCode)'Á' | KeyCode.ShiftMask)]
+	[InlineData ('à', 'À', (KeyCode)'À' | KeyCode.ShiftMask)]
+	[InlineData ('ý', 'Ý', (KeyCode)'Ý' | KeyCode.ShiftMask)]
+	[InlineData ('1', '!', (KeyCode)'!' | KeyCode.ShiftMask)]
+	[InlineData ('2', '"', (KeyCode)'"' | KeyCode.ShiftMask)]
+	[InlineData ('3', '#', (KeyCode)'#' | KeyCode.ShiftMask)]
+	[InlineData ('4', '$', (KeyCode)'$' | KeyCode.ShiftMask)]
+	[InlineData ('5', '%', (KeyCode)'%' | KeyCode.ShiftMask)]
+	[InlineData ('6', '&', (KeyCode)'&' | KeyCode.ShiftMask)]
+	[InlineData ('7', '/', (KeyCode)'/' | KeyCode.ShiftMask)]
+	[InlineData ('8', '(', (KeyCode)'(' | KeyCode.ShiftMask)]
+	[InlineData ('9', ')', (KeyCode)')' | KeyCode.ShiftMask)]
+	[InlineData ('0', '=', (KeyCode)'=' | KeyCode.ShiftMask)]
+	[InlineData ('\\', '|', (KeyCode)'|' | KeyCode.ShiftMask)]
+	[InlineData ('\'', '?', (KeyCode)'?' | KeyCode.ShiftMask)]
+	[InlineData ('«', '»', (KeyCode)'»' | KeyCode.ShiftMask)]
+	[InlineData ('+', '*', (KeyCode)'*' | KeyCode.ShiftMask)]
+	[InlineData ('´', '`', (KeyCode)'`' | KeyCode.ShiftMask)]
+	[InlineData ('º', 'ª', (KeyCode)'ª' | KeyCode.ShiftMask)]
+	[InlineData ('~', '^', (KeyCode)'^' | KeyCode.ShiftMask)]
+	[InlineData ('<', '>', (KeyCode)'>' | KeyCode.ShiftMask)]
+	[InlineData (',', ';', (KeyCode)';' | KeyCode.ShiftMask)]
+	[InlineData ('.', ':', (KeyCode)':' | KeyCode.ShiftMask)]
+	[InlineData ('-', '_', (KeyCode)'_' | KeyCode.ShiftMask)]
+	public void GetKeyChar_Shifted_Char_From_UnShifted_Char (char unicodeChar, char expectedKeyChar, KeyCode excpectedKeyCode)
+	{
+		var modifiers = ConsoleKeyMapping.GetModifiers (true, false, false);
+		var keyChar = ConsoleKeyMapping.GetKeyChar (unicodeChar, modifiers);
+		Assert.Equal (keyChar, expectedKeyChar);
+
+		KeyCode keyCode = (KeyCode)keyChar;
+		keyCode = ConsoleKeyMapping.MapToKeyCodeModifiers (modifiers, keyCode);
+
+		Assert.Equal (keyCode, excpectedKeyCode);
+	}
+
+	[Theory]
+	[InlineData ('A', 'a', (KeyCode)'a')]
+	[InlineData ('Z', 'z', (KeyCode)'z')]
+	[InlineData ('Á', 'á', (KeyCode)'á')]
+	[InlineData ('À', 'à', (KeyCode)'à')]
+	[InlineData ('Ý', 'ý', (KeyCode)'ý')]
+	[InlineData ('!', '1', KeyCode.D1)]
+	[InlineData ('"', '2', KeyCode.D2)]
+	[InlineData ('#', '3', KeyCode.D3)]
+	[InlineData ('$', '4', KeyCode.D4)]
+	[InlineData ('%', '5', KeyCode.D5)]
+	[InlineData ('&', '6', KeyCode.D6)]
+	[InlineData ('/', '7', KeyCode.D7)]
+	[InlineData ('(', '8', KeyCode.D8)]
+	[InlineData (')', '9', KeyCode.D9)]
+	[InlineData ('=', '0', KeyCode.D0)]
+	[InlineData ('|', '\\', (KeyCode)'\\')]
+	[InlineData ('?', '\'', (KeyCode)'\'')]
+	[InlineData ('»', '«', (KeyCode)'«')]
+	[InlineData ('*', '+', (KeyCode)'+')]
+	[InlineData ('`', '´', (KeyCode)'´')]
+	[InlineData ('ª', 'º', (KeyCode)'º')]
+	[InlineData ('^', '~', (KeyCode)'~')]
+	[InlineData ('>', '<', (KeyCode)'<')]
+	[InlineData (';', ',', (KeyCode)',')]
+	[InlineData (':', '.', (KeyCode)'.')]
+	[InlineData ('_', '-', (KeyCode)'-')]
+	public void GetKeyChar_UnShifted_Char_From_Shifted_Char (char unicodeChar, char expectedKeyChar, KeyCode excpectedKeyCode)
+	{
+		var modifiers = ConsoleKeyMapping.GetModifiers (false, false, false);
+		var keyChar = ConsoleKeyMapping.GetKeyChar (unicodeChar, modifiers);
+		Assert.Equal (keyChar, expectedKeyChar);
+
+		KeyCode keyCode = (KeyCode)keyChar;
+		keyCode = ConsoleKeyMapping.MapToKeyCodeModifiers (modifiers, keyCode);
+
+		Assert.Equal (keyCode, excpectedKeyCode);
+	}
 }
 }

+ 81 - 31
UnitTests/Input/KeyTests.cs

@@ -35,9 +35,10 @@ public class KeyTests {
 	[InlineData (' ', KeyCode.Space)]
 	[InlineData (' ', KeyCode.Space)]
 	[InlineData ('1', KeyCode.D1)]
 	[InlineData ('1', KeyCode.D1)]
 	[InlineData ('!', (KeyCode)'!')]
 	[InlineData ('!', (KeyCode)'!')]
-	[InlineData ('\n', KeyCode.Enter)]
+	[InlineData ('\r', KeyCode.Enter)]
 	[InlineData ('\t', KeyCode.Tab)]
 	[InlineData ('\t', KeyCode.Tab)]
 	[InlineData ('\r', (KeyCode)13)]
 	[InlineData ('\r', (KeyCode)13)]
+	[InlineData ('\n', (KeyCode)10)]
 	[InlineData ('ó', (KeyCode)'ó')]
 	[InlineData ('ó', (KeyCode)'ó')]
 	[InlineData ('Ó', (KeyCode)'Ó')]
 	[InlineData ('Ó', (KeyCode)'Ó')]
 	[InlineData ('❿', (KeyCode)'❿')]
 	[InlineData ('❿', (KeyCode)'❿')]
@@ -78,6 +79,10 @@ public class KeyTests {
 	[InlineData ("Ctrl+Alt+Tab", KeyCode.Tab | KeyCode.AltMask | KeyCode.CtrlMask)]
 	[InlineData ("Ctrl+Alt+Tab", KeyCode.Tab | KeyCode.AltMask | KeyCode.CtrlMask)]
 	[InlineData ("", KeyCode.Null)]
 	[InlineData ("", KeyCode.Null)]
 	[InlineData (" ", KeyCode.Space)]
 	[InlineData (" ", KeyCode.Space)]
+	[InlineData ("Space", KeyCode.Space)]
+	[InlineData ("Shift+Space", KeyCode.Space | KeyCode.ShiftMask)]
+	[InlineData ("Ctrl+Space", KeyCode.Space | KeyCode.CtrlMask)]
+	[InlineData ("Alt+Space", KeyCode.Space | KeyCode.AltMask)]
 	[InlineData ("Shift+ ", KeyCode.Space | KeyCode.ShiftMask)]
 	[InlineData ("Shift+ ", KeyCode.Space | KeyCode.ShiftMask)]
 	[InlineData ("Ctrl+ ", KeyCode.Space | KeyCode.CtrlMask)]
 	[InlineData ("Ctrl+ ", KeyCode.Space | KeyCode.CtrlMask)]
 	[InlineData ("Alt+ ", KeyCode.Space | KeyCode.AltMask)]
 	[InlineData ("Alt+ ", KeyCode.Space | KeyCode.AltMask)]
@@ -87,8 +92,8 @@ public class KeyTests {
 	[InlineData ("D0", KeyCode.D0)]
 	[InlineData ("D0", KeyCode.D0)]
 	[InlineData ("65", KeyCode.A | KeyCode.ShiftMask)]
 	[InlineData ("65", KeyCode.A | KeyCode.ShiftMask)]
 	[InlineData ("97", KeyCode.A)]
 	[InlineData ("97", KeyCode.A)]
-	[InlineData ("Shift", KeyCode.ShiftKey)]
-	[InlineData ("Ctrl", KeyCode.CtrlKey)]
+	[InlineData ("Shift", KeyCode.ShiftMask)]
+	[InlineData ("Ctrl", KeyCode.CtrlMask)]
 	[InlineData ("Ctrl-A", KeyCode.A | KeyCode.CtrlMask)]
 	[InlineData ("Ctrl-A", KeyCode.A | KeyCode.CtrlMask)]
 	[InlineData ("Alt-A", KeyCode.A | KeyCode.AltMask)]
 	[InlineData ("Alt-A", KeyCode.A | KeyCode.AltMask)]
 	[InlineData ("A-Ctrl", KeyCode.A | KeyCode.CtrlMask)]
 	[InlineData ("A-Ctrl", KeyCode.A | KeyCode.CtrlMask)]
@@ -114,9 +119,10 @@ public class KeyTests {
 	[InlineData (' ', KeyCode.Space)]
 	[InlineData (' ', KeyCode.Space)]
 	[InlineData ('1', KeyCode.D1)]
 	[InlineData ('1', KeyCode.D1)]
 	[InlineData ('!', (KeyCode)'!')]
 	[InlineData ('!', (KeyCode)'!')]
-	[InlineData ('\n', KeyCode.Enter)]
+	[InlineData ('\r', KeyCode.Enter)]
 	[InlineData ('\t', KeyCode.Tab)]
 	[InlineData ('\t', KeyCode.Tab)]
 	[InlineData ('\r', (KeyCode)13)]
 	[InlineData ('\r', (KeyCode)13)]
+	[InlineData ('\n', (KeyCode)10)]
 	[InlineData ('ó', (KeyCode)'ó')]
 	[InlineData ('ó', (KeyCode)'ó')]
 	[InlineData ('Ó', (KeyCode)'Ó')]
 	[InlineData ('Ó', (KeyCode)'Ó')]
 	[InlineData ('❿', (KeyCode)'❿')]
 	[InlineData ('❿', (KeyCode)'❿')]
@@ -157,7 +163,7 @@ public class KeyTests {
 	[InlineData (KeyCode.CtrlMask, false)]
 	[InlineData (KeyCode.CtrlMask, false)]
 	[InlineData (KeyCode.AltMask, false)]
 	[InlineData (KeyCode.AltMask, false)]
 	[InlineData (KeyCode.ShiftMask | KeyCode.AltMask, false)]
 	[InlineData (KeyCode.ShiftMask | KeyCode.AltMask, false)]
-	public void IsValid (Key key, bool expected) => Assert.Equal (expected, key.IsValid);
+	public void IsValid (KeyCode key, bool expected) => Assert.Equal (expected, ((Key)key).IsValid);
 
 
 	[Fact]
 	[Fact]
 	public void HandledProperty_ShouldBeFalseByDefault ()
 	public void HandledProperty_ShouldBeFalseByDefault ()
@@ -173,15 +179,15 @@ public class KeyTests {
 	[InlineData (KeyCode.A | KeyCode.ShiftMask, KeyCode.A | KeyCode.ShiftMask)]
 	[InlineData (KeyCode.A | KeyCode.ShiftMask, KeyCode.A | KeyCode.ShiftMask)]
 	[InlineData (KeyCode.Z, (KeyCode)'z')]
 	[InlineData (KeyCode.Z, (KeyCode)'z')]
 	[InlineData (KeyCode.Space, KeyCode.Space)]
 	[InlineData (KeyCode.Space, KeyCode.Space)]
-	public void Cast_KeyCode_To_Key (KeyCode cdk, Key expected)
+	public void Cast_KeyCode_To_Key (KeyCode cdk, KeyCode expected)
 	{
 	{
 		// explicit
 		// explicit
 		var key = (Key)cdk;
 		var key = (Key)cdk;
-		Assert.Equal (expected.ToString (), key.ToString ());
+		Assert.Equal (((Key)expected).ToString (), key.ToString ());
 
 
 		// implicit
 		// implicit
 		key = cdk;
 		key = cdk;
-		Assert.Equal (expected.ToString (), key.ToString ());
+		Assert.Equal (((Key)expected).ToString (), key.ToString ());
 	}
 	}
 
 
 	[Fact]
 	[Fact]
@@ -256,7 +262,7 @@ public class KeyTests {
 	[InlineData (KeyCode.F1, '\0')]
 	[InlineData (KeyCode.F1, '\0')]
 	[InlineData (KeyCode.ShiftMask | KeyCode.F1, '\0')]
 	[InlineData (KeyCode.ShiftMask | KeyCode.F1, '\0')]
 	[InlineData (KeyCode.CtrlMask | KeyCode.F1, '\0')]
 	[InlineData (KeyCode.CtrlMask | KeyCode.F1, '\0')]
-	[InlineData (KeyCode.Enter, '\n')]
+	[InlineData (KeyCode.Enter, '\r')]
 	[InlineData (KeyCode.Tab, '\t')]
 	[InlineData (KeyCode.Tab, '\t')]
 	[InlineData (KeyCode.Esc, 0x1b)]
 	[InlineData (KeyCode.Esc, 0x1b)]
 	[InlineData (KeyCode.Space, ' ')]
 	[InlineData (KeyCode.Space, ' ')]
@@ -265,10 +271,10 @@ public class KeyTests {
 	[InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.Null, '\0')]
 	[InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.Null, '\0')]
 	[InlineData (KeyCode.CharMask, '\0')]
 	[InlineData (KeyCode.CharMask, '\0')]
 	[InlineData (KeyCode.SpecialMask, '\0')]
 	[InlineData (KeyCode.SpecialMask, '\0')]
-	public void AsRune_ShouldReturnCorrectIntValue (KeyCode key, Rune expected)
+	public void AsRune_ShouldReturnCorrectIntValue (KeyCode key, uint expected)
 	{
 	{
 		var eventArgs = new Key (key);
 		var eventArgs = new Key (key);
-		Assert.Equal (expected, eventArgs.AsRune);
+		Assert.Equal ((Rune)expected, eventArgs.AsRune);
 	}
 	}
 
 
 	[Theory]
 	[Theory]
@@ -316,7 +322,7 @@ public class KeyTests {
 	}
 	}
 
 
 	// TODO: Create equality operator for KeyCode
 	// TODO: Create equality operator for KeyCode
-	//Assert.Equal (KeyCode.Delete, Key.Delete);
+	//Assert.Equal (KeyCode.DeleteChar, Key.Delete);
 
 
 	// Similar tests for IsShift and IsCtrl
 	// Similar tests for IsShift and IsCtrl
 	[Fact]
 	[Fact]
@@ -333,11 +339,11 @@ public class KeyTests {
 
 
 	[Theory]
 	[Theory]
 	[InlineData ((KeyCode)'☑', "☑")]
 	[InlineData ((KeyCode)'☑', "☑")]
-	//[InlineData ((ConsoleDriverKey)'英', "英")]
-	//[InlineData ((ConsoleDriverKey)'{', "{")]
+	[InlineData ((KeyCode)'英', "英")]
+	[InlineData ((KeyCode)'{', "{")]
 	[InlineData ((KeyCode)'\'', "\'")]
 	[InlineData ((KeyCode)'\'', "\'")]
 	[InlineData ((KeyCode)'ó', "ó")]
 	[InlineData ((KeyCode)'ó', "ó")]
-	[InlineData ((KeyCode)'ó' | KeyCode.ShiftMask, "Shift+ó")] // is this right???
+	[InlineData ((KeyCode)'Ó' | KeyCode.ShiftMask, "Shift+Ó")] // TODO: This is not correct, it should be Shift+ó or just Ó
 	[InlineData ((KeyCode)'Ó', "Ó")]
 	[InlineData ((KeyCode)'Ó', "Ó")]
 	[InlineData ((KeyCode)'ç' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, "Ctrl+Alt+Shift+ç")]
 	[InlineData ((KeyCode)'ç' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, "Ctrl+Alt+Shift+ç")]
 	[InlineData ((KeyCode)'a', "a")] // 97 or Key.Space | Key.A
 	[InlineData ((KeyCode)'a', "a")] // 97 or Key.Space | Key.A
@@ -397,19 +403,33 @@ public class KeyTests {
 	[InlineData (KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CursorUp, "Alt+Shift+CursorUp")]
 	[InlineData (KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CursorUp, "Alt+Shift+CursorUp")]
 	[InlineData (KeyCode.AltMask | KeyCode.CtrlMask | KeyCode.CursorUp, "Ctrl+Alt+CursorUp")]
 	[InlineData (KeyCode.AltMask | KeyCode.CtrlMask | KeyCode.CursorUp, "Ctrl+Alt+CursorUp")]
 	[InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.CursorUp, "Ctrl+Alt+Shift+CursorUp")]
 	[InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.CursorUp, "Ctrl+Alt+Shift+CursorUp")]
+	[InlineData (KeyCode.Space, "Space")]
 	[InlineData (KeyCode.Null, "Null")]
 	[InlineData (KeyCode.Null, "Null")]
-	[InlineData (KeyCode.ShiftMask | KeyCode.Null, "Null")]
-	[InlineData (KeyCode.CtrlMask | KeyCode.Null, "Null")]
-	[InlineData (KeyCode.AltMask | KeyCode.Null, "Null")]
-	[InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.Null, "Null")]
-	[InlineData (KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.Null, "Null")]
-	[InlineData (KeyCode.AltMask | KeyCode.CtrlMask | KeyCode.Null, "Null")]
-	[InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.Null, "Null")]
-	[InlineData (KeyCode.AltKey, "AltKey")]
-	[InlineData (KeyCode.CtrlKey, "CtrlKey")]
-	[InlineData (KeyCode.ShiftKey, "ShiftKey")]
+	[InlineData (KeyCode.ShiftMask | KeyCode.Null, "Shift")]
+	[InlineData (KeyCode.CtrlMask | KeyCode.Null, "Ctrl")]
+	[InlineData (KeyCode.AltMask | KeyCode.Null, "Alt")]
+	[InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.Null, "Ctrl+Shift")]
+	[InlineData (KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.Null, "Alt+Shift")]
+	[InlineData (KeyCode.AltMask | KeyCode.CtrlMask | KeyCode.Null, "Ctrl+Alt")]
+	[InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.Null, "Ctrl+Alt+Shift")]
+#pragma warning disable xUnit1025 // InlineData should be unique within the Theory it belongs to
+	[InlineData (KeyCode.ShiftMask, "Shift")]
+	[InlineData (KeyCode.CtrlMask, "Ctrl")]
+	[InlineData (KeyCode.AltMask, "Alt")]
+	[InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask, "Ctrl+Shift")]
+	[InlineData (KeyCode.ShiftMask | KeyCode.AltMask, "Alt+Shift")]
+	[InlineData (KeyCode.AltMask | KeyCode.CtrlMask, "Ctrl+Alt")]
+	[InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.AltMask, "Ctrl+Alt+Shift")]
+#pragma warning restore xUnit1025 // InlineData should be unique within the Theory it belongs to
+	[InlineData (KeyCode.AltMask, "Alt")]
+	[InlineData (KeyCode.CtrlMask, "Ctrl")]
+	[InlineData (KeyCode.ShiftMask, "Shift")]
 	[InlineData (KeyCode.CharMask, "CharMask")]
 	[InlineData (KeyCode.CharMask, "CharMask")]
 	[InlineData (KeyCode.SpecialMask, "Ctrl+Alt+Shift")]
 	[InlineData (KeyCode.SpecialMask, "Ctrl+Alt+Shift")]
+	[InlineData ((KeyCode)'+', "+")]
+	[InlineData ((KeyCode)'+' | KeyCode.ShiftMask, "Shift++")]
+	[InlineData ((KeyCode)'+' | KeyCode.CtrlMask, "Ctrl++")] 
+	[InlineData ((KeyCode)'+' | KeyCode.ShiftMask | KeyCode.CtrlMask, "Ctrl+Shift++")]
 	public void ToString_ShouldReturnFormattedString (KeyCode key, string expected) => Assert.Equal (expected, Key.ToString (key));
 	public void ToString_ShouldReturnFormattedString (KeyCode key, string expected) => Assert.Equal (expected, Key.ToString (key));
 
 
 	// TryParse
 	// TryParse
@@ -436,6 +456,10 @@ public class KeyTests {
 	[InlineData ("Ctrl+Alt+Tab", KeyCode.Tab | KeyCode.AltMask | KeyCode.CtrlMask)]
 	[InlineData ("Ctrl+Alt+Tab", KeyCode.Tab | KeyCode.AltMask | KeyCode.CtrlMask)]
 	[InlineData ("", KeyCode.Null)]
 	[InlineData ("", KeyCode.Null)]
 	[InlineData (" ", KeyCode.Space)]
 	[InlineData (" ", KeyCode.Space)]
+	[InlineData ("Space", KeyCode.Space)]
+	[InlineData ("Shift+Space", KeyCode.Space | KeyCode.ShiftMask)]
+	[InlineData ("Ctrl+Space", KeyCode.Space | KeyCode.CtrlMask)]
+	[InlineData ("Alt+Space", KeyCode.Space | KeyCode.AltMask)]
 	[InlineData ("Shift+ ", KeyCode.Space | KeyCode.ShiftMask)]
 	[InlineData ("Shift+ ", KeyCode.Space | KeyCode.ShiftMask)]
 	[InlineData ("Ctrl+ ", KeyCode.Space | KeyCode.CtrlMask)]
 	[InlineData ("Ctrl+ ", KeyCode.Space | KeyCode.CtrlMask)]
 	[InlineData ("Alt+ ", KeyCode.Space | KeyCode.AltMask)]
 	[InlineData ("Alt+ ", KeyCode.Space | KeyCode.AltMask)]
@@ -445,17 +469,17 @@ public class KeyTests {
 	[InlineData ("D0", KeyCode.D0)]
 	[InlineData ("D0", KeyCode.D0)]
 	[InlineData ("65", KeyCode.A | KeyCode.ShiftMask)]
 	[InlineData ("65", KeyCode.A | KeyCode.ShiftMask)]
 	[InlineData ("97", KeyCode.A)]
 	[InlineData ("97", KeyCode.A)]
-	[InlineData ("Shift", KeyCode.ShiftKey)]
-	[InlineData ("Ctrl", KeyCode.CtrlKey)]
+	[InlineData ("Shift", KeyCode.ShiftMask)]
+	[InlineData ("Ctrl", KeyCode.CtrlMask)]
 	[InlineData ("Ctrl-A", KeyCode.A | KeyCode.CtrlMask)]
 	[InlineData ("Ctrl-A", KeyCode.A | KeyCode.CtrlMask)]
 	[InlineData ("Alt-A", KeyCode.A | KeyCode.AltMask)]
 	[InlineData ("Alt-A", KeyCode.A | KeyCode.AltMask)]
 	[InlineData ("A-Ctrl", KeyCode.A | KeyCode.CtrlMask)]
 	[InlineData ("A-Ctrl", KeyCode.A | KeyCode.CtrlMask)]
 	[InlineData ("Alt-A-Ctrl", KeyCode.A | KeyCode.CtrlMask | KeyCode.AltMask)]
 	[InlineData ("Alt-A-Ctrl", KeyCode.A | KeyCode.CtrlMask | KeyCode.AltMask)]
-	public void TryParse_ShouldReturnTrue_WhenValidKey (string keyString, Key expected)
+	public void TryParse_ShouldReturnTrue_WhenValidKey (string keyString, KeyCode expected)
 	{
 	{
-		Key key;
-		Assert.True (Key.TryParse (keyString, out key));
-		Assert.Equal (((Key)expected).ToString (), key.ToString ());
+		Assert.True (Key.TryParse (keyString, out Key key));
+		Key expectedKey = (Key)expected;
+		Assert.Equal (expectedKey.ToString (), key.ToString ());
 	}
 	}
 
 
 	[Theory]
 	[Theory]
@@ -471,4 +495,30 @@ public class KeyTests {
 	[InlineData ("0x99")]
 	[InlineData ("0x99")]
 	[InlineData ("Ctrl-Ctrl")]
 	[InlineData ("Ctrl-Ctrl")]
 	public void TryParse_ShouldReturnFalse_On_InvalidKey (string keyString) => Assert.False (Key.TryParse (keyString, out var _));
 	public void TryParse_ShouldReturnFalse_On_InvalidKey (string keyString) => Assert.False (Key.TryParse (keyString, out var _));
+	
+	[Theory]
+	[InlineData (KeyCode.ShiftMask, true, false, false)]
+	[InlineData (KeyCode.ShiftMask | KeyCode.AltMask, true, true, false)]
+	[InlineData (KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, true, true, true)]
+	[InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask, true, false, true)]
+	[InlineData (KeyCode.AltMask, false, true, false)]
+	[InlineData (KeyCode.AltMask | KeyCode.CtrlMask, false, true, true)]
+	[InlineData (KeyCode.CtrlMask, false, false, true)]
+	public void IsShift_IsAlt_IsCtrl (KeyCode keyCode, bool isShift, bool isAlt, bool isCtrl)
+	{
+		Assert.Equal (((Key)keyCode).IsShift, isShift);
+		Assert.Equal (((Key)keyCode).IsAlt, isAlt);
+		Assert.Equal (((Key)keyCode).IsCtrl, isCtrl);
+	}
+
+	[Theory]
+	[InlineData (KeyCode.A, KeyCode.A)]
+	[InlineData (KeyCode.F1, KeyCode.F1)]
+	public void Casting_Between_Key_And_KeyCode (KeyCode keyCode, KeyCode key)
+	{
+		Assert.Equal (keyCode, (Key)key);
+		Assert.NotEqual (keyCode, ((Key)key).WithShift);
+		Assert.Equal ((uint)keyCode, (uint)(Key)key);
+		Assert.NotEqual ((uint)keyCode, (uint)((Key)key).WithShift);
+	}
 }
 }

+ 17 - 18
UnitTests/Text/CollectionNavigatorTests.cs

@@ -383,22 +383,21 @@ public class CollectionNavigatorTests {
 
 
 		Assert.Equal (-1, current = n.GetNextMatchingItem (current, "x", true));
 		Assert.Equal (-1, current = n.GetNextMatchingItem (current, "x", true));
 	}
 	}
-
-	[Fact]
-	public void IsCompatibleKey_Does_Not_Allow_Alt_And_Ctrl_Keys ()
-	{
-		// test all Keys
-		foreach (KeyCode key in Enum.GetValues (typeof (KeyCode))) {
-			var ke = new Key (key);
-			_output.WriteLine ($"Testing {key}");
-			if (key == KeyCode.AltMask || key == KeyCode.CtrlMask || key == KeyCode.SpecialMask) {
-				Assert.False (CollectionNavigator.IsCompatibleKey (ke));
-			} else {
-				Assert.True (CollectionNavigator.IsCompatibleKey (ke));
-			}
-		}
-
-		// test Capslock, Numlock and Scrolllock
-		Assert.True (CollectionNavigator.IsCompatibleKey (new (KeyCode.Null)));
-	}
+	
+	[Theory]
+	[InlineData (KeyCode.A, true)]
+	[InlineData (KeyCode.Z, true)]
+	[InlineData (KeyCode.D0, true)]
+	[InlineData (KeyCode.A | KeyCode.ShiftMask, true)]
+	[InlineData (KeyCode.Z | KeyCode.ShiftMask, true)]
+	[InlineData (KeyCode.Space, true)]
+
+	[InlineData (KeyCode.Z | KeyCode.CtrlMask, false)]
+	[InlineData (KeyCode.Z | KeyCode.AltMask, false)]
+	[InlineData (KeyCode.F1, false)]
+	[InlineData (KeyCode.Delete, false)]
+	[InlineData (KeyCode.Delete, false)]
+	[InlineData (KeyCode.Esc, false)]
+	[InlineData (KeyCode.ShiftMask, false)]
+	public void IsCompatibleKey_Does_Not_Allow_Alt_And_Ctrl_Keys (KeyCode keyCode, bool compatible) => Assert.Equal (compatible, CollectionNavigatorBase.IsCompatibleKey (keyCode));
 }
 }

+ 12 - 12
UnitTests/Text/TextFormatterTests.cs

@@ -249,7 +249,7 @@ namespace Terminal.Gui.TextTests {
 		[InlineData ("After K_", false, -1, KeyCode.Null, true)]
 		[InlineData ("After K_", false, -1, KeyCode.Null, true)]
 		[InlineData ("Multiple _K and _R", true, 9, (KeyCode)'K', true)]
 		[InlineData ("Multiple _K and _R", true, 9, (KeyCode)'K', true)]
 		[InlineData ("Non-english: _Кдать", true, 13, (KeyCode)'К', true)] // Cryllic K (К)
 		[InlineData ("Non-english: _Кдать", true, 13, (KeyCode)'К', true)] // Cryllic K (К)
-		public void FindHotKey_AlphaUpperCase_Succeeds (string text, bool expectedResult, int expectedHotPos, Key expectedKey, bool supportFirstUpperCase = false)
+		public void FindHotKey_AlphaUpperCase_Succeeds (string text, bool expectedResult, int expectedHotPos, KeyCode expectedKey, bool supportFirstUpperCase = false)
 		{
 		{
 			Rune hotKeySpecifier = (Rune)'_';
 			Rune hotKeySpecifier = (Rune)'_';
 
 
@@ -261,7 +261,7 @@ namespace Terminal.Gui.TextTests {
 			}
 			}
 			Assert.Equal (expectedResult, result);
 			Assert.Equal (expectedResult, result);
 			Assert.Equal (expectedHotPos, hotPos);
 			Assert.Equal (expectedHotPos, hotPos);
-			Assert.Equal (expectedKey, hotKey);
+			Assert.Equal ((Key)expectedKey, hotKey);
 		}
 		}
 
 
 		[Theory]
 		[Theory]
@@ -277,7 +277,7 @@ namespace Terminal.Gui.TextTests {
 		[InlineData ("After k_", false, -1, KeyCode.Null, true)]
 		[InlineData ("After k_", false, -1, KeyCode.Null, true)]
 		[InlineData ("Multiple _k and _r", true, 9, (KeyCode)'K', true)]
 		[InlineData ("Multiple _k and _r", true, 9, (KeyCode)'K', true)]
 		[InlineData ("Non-english: _кдать", true, 13, (KeyCode)'к', true)] // Cryllic K (К)
 		[InlineData ("Non-english: _кдать", true, 13, (KeyCode)'к', true)] // Cryllic K (К)
-		public void FindHotKey_AlphaLowerCase_Succeeds (string text, bool expectedResult, int expectedHotPos, Key expectedKey, bool supportFirstUpperCase = false)
+		public void FindHotKey_AlphaLowerCase_Succeeds (string text, bool expectedResult, int expectedHotPos, KeyCode expectedKey, bool supportFirstUpperCase = false)
 		{
 		{
 			Rune hotKeySpecifier = (Rune)'_';
 			Rune hotKeySpecifier = (Rune)'_';
 
 
@@ -289,7 +289,7 @@ namespace Terminal.Gui.TextTests {
 			}
 			}
 			Assert.Equal (expectedResult, result);
 			Assert.Equal (expectedResult, result);
 			Assert.Equal (expectedHotPos, hotPos);
 			Assert.Equal (expectedHotPos, hotPos);
-			Assert.Equal (expectedKey, hotKey);
+			Assert.Equal ((Key)expectedKey, hotKey);
 		}
 		}
 
 
 		[Theory]
 		[Theory]
@@ -303,7 +303,7 @@ namespace Terminal.Gui.TextTests {
 		[InlineData ("Last _1", true, 5, (KeyCode)'1', true)]
 		[InlineData ("Last _1", true, 5, (KeyCode)'1', true)]
 		[InlineData ("After 1_", false, -1, KeyCode.Null, true)]
 		[InlineData ("After 1_", false, -1, KeyCode.Null, true)]
 		[InlineData ("Multiple _1 and _2", true, 9, (KeyCode)'1', true)]
 		[InlineData ("Multiple _1 and _2", true, 9, (KeyCode)'1', true)]
-		public void FindHotKey_Numeric_Succeeds (string text, bool expectedResult, int expectedHotPos, Key expectedKey, bool supportFirstUpperCase = false)
+		public void FindHotKey_Numeric_Succeeds (string text, bool expectedResult, int expectedHotPos, KeyCode expectedKey, bool supportFirstUpperCase = false)
 		{
 		{
 			Rune hotKeySpecifier = (Rune)'_';
 			Rune hotKeySpecifier = (Rune)'_';
 
 
@@ -315,7 +315,7 @@ namespace Terminal.Gui.TextTests {
 			}
 			}
 			Assert.Equal (expectedResult, result);
 			Assert.Equal (expectedResult, result);
 			Assert.Equal (expectedHotPos, hotPos);
 			Assert.Equal (expectedHotPos, hotPos);
-			Assert.Equal (expectedKey, hotKey);
+			Assert.Equal ((Key)expectedKey, hotKey);
 		}
 		}
 
 
 		[Theory]
 		[Theory]
@@ -324,7 +324,7 @@ namespace Terminal.Gui.TextTests {
 		[InlineData ("last K", true, 5, (KeyCode)'K')]
 		[InlineData ("last K", true, 5, (KeyCode)'K')]
 		[InlineData ("multiple K and R", true, 9, (KeyCode)'K')]
 		[InlineData ("multiple K and R", true, 9, (KeyCode)'K')]
 		[InlineData ("non-english: Кдать", true, 13, (KeyCode)'К')] // Cryllic K (К)
 		[InlineData ("non-english: Кдать", true, 13, (KeyCode)'К')] // Cryllic K (К)
-		public void FindHotKey_Legacy_FirstUpperCase_Succeeds (string text, bool expectedResult, int expectedHotPos, Key expectedKey)
+		public void FindHotKey_Legacy_FirstUpperCase_Succeeds (string text, bool expectedResult, int expectedHotPos, KeyCode expectedKey)
 		{
 		{
 			var supportFirstUpperCase = true;
 			var supportFirstUpperCase = true;
 
 
@@ -338,25 +338,25 @@ namespace Terminal.Gui.TextTests {
 			}
 			}
 			Assert.Equal (expectedResult, result);
 			Assert.Equal (expectedResult, result);
 			Assert.Equal (expectedHotPos, hotPos);
 			Assert.Equal (expectedHotPos, hotPos);
-			Assert.Equal (expectedKey, hotKey);
+			Assert.Equal ((Key)expectedKey, hotKey);
 		}
 		}
 		
 		
 		[Theory]
 		[Theory]
-		//[InlineData ("_\"k before", false, Key.Null)] // BUGBUG: Not sure why this fails
+		[InlineData ("_\"k before", true, (KeyCode)'"')] // BUGBUG: Not sure why this fails. " is a normal char
 		[InlineData ("\"_k before", true, KeyCode.K)]
 		[InlineData ("\"_k before", true, KeyCode.K)]
 		[InlineData ("_`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?", true, (KeyCode)'`')]
 		[InlineData ("_`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?", true, (KeyCode)'`')]
 		[InlineData ("`_~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?", true, (KeyCode)'~')]
 		[InlineData ("`_~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?", true, (KeyCode)'~')]
-		//[InlineData ("`~!@#$%^&*()-__=+[{]}\\|;:'\",<.>/?", true, (Key)'_')] // BUGBUG: Not sure why this fails
+		[InlineData ("`~!@#$%^&*()-__=+[{]}\\|;:'\",<.>/?", true, (KeyCode)'=')] // BUGBUG: Not sure why this fails. Ignore the first and consider the second
 		[InlineData ("_ ~  s  gui.cs   master ↑10", true, (KeyCode)'')] // ~IsLetterOrDigit + Unicode
 		[InlineData ("_ ~  s  gui.cs   master ↑10", true, (KeyCode)'')] // ~IsLetterOrDigit + Unicode
 		[InlineData (" ~  s  gui.cs  _ master ↑10", true, (KeyCode)'')] // ~IsLetterOrDigit + Unicode
 		[InlineData (" ~  s  gui.cs  _ master ↑10", true, (KeyCode)'')] // ~IsLetterOrDigit + Unicode
 		[InlineData ("non-english: _кдать", true, (KeyCode)'к')] // Lower case Cryllic K (к)
 		[InlineData ("non-english: _кдать", true, (KeyCode)'к')] // Lower case Cryllic K (к)
-		public void FindHotKey_Symbols_Returns_Symbol (string text, bool found, Key expected)
+		public void FindHotKey_Symbols_Returns_Symbol (string text, bool found, KeyCode expected)
 		{
 		{
 			var hotKeySpecifier = (Rune)'_';
 			var hotKeySpecifier = (Rune)'_';
 
 
 			var result = TextFormatter.FindHotKey (text, hotKeySpecifier, false, out int _, out var hotKey);
 			var result = TextFormatter.FindHotKey (text, hotKeySpecifier, false, out int _, out var hotKey);
 			Assert.Equal (found, result);
 			Assert.Equal (found, result);
-			Assert.Equal (expected, hotKey);
+			Assert.Equal ((Key)expected, hotKey);
 		}
 		}
 
 
 		[Theory]
 		[Theory]

+ 12 - 12
UnitTests/View/HotKeyTests.cs

@@ -51,37 +51,37 @@ public class HotKeyTests {
 	[InlineData ((KeyCode)'х')]  // Cyrillic x
 	[InlineData ((KeyCode)'х')]  // Cyrillic x
 	[InlineData ((KeyCode)'你')] // Chinese ni
 	[InlineData ((KeyCode)'你')] // Chinese ni
 	[InlineData ((KeyCode)'ö')] // German o umlaut
 	[InlineData ((KeyCode)'ö')] // German o umlaut
-	public void Set_SetsKeyBindings (Key key)
+	public void Set_SetsKeyBindings (KeyCode key)
 	{
 	{
 		var view = new View ();
 		var view = new View ();
-		view.HotKey = key;
+		view.HotKey = (Key)key;
 		Assert.Equal (string.Empty, view.Title);
 		Assert.Equal (string.Empty, view.Title);
-		Assert.Equal (key, view.HotKey);
+		Assert.Equal ((Key)key, view.HotKey);
 
 
 		// Verify key bindings were set
 		// Verify key bindings were set
 
 
 		// As passed
 		// As passed
-		var commands = view.KeyBindings.GetCommands (key);
+		var commands = view.KeyBindings.GetCommands ((Key)key);
 		Assert.Contains (Command.Accept, commands);
 		Assert.Contains (Command.Accept, commands);
 
 
-		var baseKey = key.NoShift;
+		var baseKey = ((Key)key).NoShift;
 		// If A...Z, with and without shift
 		// If A...Z, with and without shift
 		if (baseKey.IsKeyCodeAtoZ) {
 		if (baseKey.IsKeyCodeAtoZ) {
-			commands = view.KeyBindings.GetCommands (key.WithShift);
+			commands = view.KeyBindings.GetCommands (((Key)key).WithShift);
 			Assert.Contains (Command.Accept, commands);
 			Assert.Contains (Command.Accept, commands);
-			commands = view.KeyBindings.GetCommands (key.NoShift);
+			commands = view.KeyBindings.GetCommands (((Key)key).NoShift);
 			Assert.Contains (Command.Accept, commands);
 			Assert.Contains (Command.Accept, commands);
-			commands = view.KeyBindings.GetCommands (key.WithAlt);
+			commands = view.KeyBindings.GetCommands (((Key)key).WithAlt);
 			Assert.Contains (Command.Accept, commands);
 			Assert.Contains (Command.Accept, commands);
-			commands = view.KeyBindings.GetCommands (key.NoShift.WithAlt);
+			commands = view.KeyBindings.GetCommands (((Key)key).NoShift.WithAlt);
 			Assert.Contains (Command.Accept, commands);
 			Assert.Contains (Command.Accept, commands);
 		} else {
 		} else {
 			// Non A..Z keys should not have shift bindings
 			// Non A..Z keys should not have shift bindings
-			if (key.IsShift) {
-				commands = view.KeyBindings.GetCommands (key.NoShift);
+			if (((Key)key).IsShift) {
+				commands = view.KeyBindings.GetCommands (((Key)key).NoShift);
 				Assert.Empty (commands);
 				Assert.Empty (commands);
 			} else {
 			} else {
-				commands = view.KeyBindings.GetCommands (key.WithShift);
+				commands = view.KeyBindings.GetCommands (((Key)key).WithShift);
 				Assert.Empty (commands);
 				Assert.Empty (commands);
 			}
 			}
 		}
 		}

+ 1 - 1
UnitTests/Views/DateFieldTests.cs

@@ -64,7 +64,7 @@ namespace Terminal.Gui.ViewsTests {
 			CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
 			CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
 			DateField df = new DateField (DateTime.Parse ("12/12/1971"));
 			DateField df = new DateField (DateTime.Parse ("12/12/1971"));
 			df.ReadOnly = true;
 			df.ReadOnly = true;
-			Assert.True (df.NewKeyDownEvent (new (KeyCode.DeleteChar)));
+			Assert.True (df.NewKeyDownEvent (new (KeyCode.Delete)));
 			Assert.Equal (" 12/12/1971", df.Text);
 			Assert.Equal (" 12/12/1971", df.Text);
 			df.ReadOnly = false;
 			df.ReadOnly = false;
 			Assert.True (df.NewKeyDownEvent (new (KeyCode.D | KeyCode.CtrlMask)));
 			Assert.True (df.NewKeyDownEvent (new (KeyCode.D | KeyCode.CtrlMask)));

+ 5 - 5
UnitTests/Views/TextFieldTests.cs

@@ -950,10 +950,10 @@ public class TextFieldTests {
 		Assert.Equal (15, tf.CursorPosition);
 		Assert.Equal (15, tf.CursorPosition);
 		Assert.False (tf.ReadOnly);
 		Assert.False (tf.ReadOnly);
 
 
-		Assert.True (tf.NewKeyDownEvent (new (KeyCode.DeleteChar)));
+		Assert.True (tf.NewKeyDownEvent (new (KeyCode.Delete)));
 		Assert.Equal ("This is a test.", tf.Text);
 		Assert.Equal ("This is a test.", tf.Text);
 		tf.CursorPosition = 0;
 		tf.CursorPosition = 0;
-		Assert.True (tf.NewKeyDownEvent (new (KeyCode.DeleteChar)));
+		Assert.True (tf.NewKeyDownEvent (new (KeyCode.Delete)));
 		Assert.Equal ("his is a test.", tf.Text);
 		Assert.Equal ("his is a test.", tf.Text);
 		tf.ReadOnly = true;
 		tf.ReadOnly = true;
 		Assert.True (tf.NewKeyDownEvent (new (KeyCode.D | KeyCode.CtrlMask)));
 		Assert.True (tf.NewKeyDownEvent (new (KeyCode.D | KeyCode.CtrlMask)));
@@ -1114,7 +1114,7 @@ public class TextFieldTests {
 		Assert.Equal ("is is a t", tf.Text);
 		Assert.Equal ("is is a t", tf.Text);
 		Assert.Equal (9, tf.CursorPosition);
 		Assert.Equal (9, tf.CursorPosition);
 		Assert.True (tf.Used);
 		Assert.True (tf.Used);
-		Assert.True (tf.NewKeyDownEvent (new (KeyCode.InsertChar)));
+		Assert.True (tf.NewKeyDownEvent (new (KeyCode.Insert)));
 		Assert.Equal ("is is a t", tf.Text);
 		Assert.Equal ("is is a t", tf.Text);
 		Assert.Equal (9, tf.CursorPosition);
 		Assert.Equal (9, tf.CursorPosition);
 		Assert.False (tf.Used);
 		Assert.False (tf.Used);
@@ -1137,7 +1137,7 @@ public class TextFieldTests {
 		Assert.Equal ("is is a", Clipboard.Contents);
 		Assert.Equal ("is is a", Clipboard.Contents);
 		tf.Text = "TAB to jump between text fields.";
 		tf.Text = "TAB to jump between text fields.";
 		Assert.Equal (0, tf.CursorPosition);
 		Assert.Equal (0, tf.CursorPosition);
-		Assert.True (tf.NewKeyDownEvent (new (KeyCode.DeleteChar | KeyCode.CtrlMask)));
+		Assert.True (tf.NewKeyDownEvent (new (KeyCode.Delete | KeyCode.CtrlMask)));
 		Assert.Equal ("to jump between text fields.", tf.Text);
 		Assert.Equal ("to jump between text fields.", tf.Text);
 		tf.CursorPosition = tf.Text.Length;
 		tf.CursorPosition = tf.Text.Length;
 		Assert.True (tf.NewKeyDownEvent (new (KeyCode.Backspace | KeyCode.CtrlMask)));
 		Assert.True (tf.NewKeyDownEvent (new (KeyCode.Backspace | KeyCode.CtrlMask)));
@@ -1212,7 +1212,7 @@ public class TextFieldTests {
 		tf.CursorPosition = 2;
 		tf.CursorPosition = 2;
 		Assert.Equal (1, tf.SelectedLength);
 		Assert.Equal (1, tf.SelectedLength);
 		Assert.Equal ("1", tf.SelectedText);
 		Assert.Equal ("1", tf.SelectedText);
-		Assert.True (tf.NewKeyDownEvent (new (KeyCode.DeleteChar)));
+		Assert.True (tf.NewKeyDownEvent (new (KeyCode.Delete)));
 		Assert.Equal ("-", newText);
 		Assert.Equal ("-", newText);
 		Assert.Equal ("-1", oldText);
 		Assert.Equal ("-1", oldText);
 		Assert.Equal ("-", tf.Text);
 		Assert.Equal ("-", tf.Text);

+ 19 - 20
UnitTests/Views/TextViewTests.cs

@@ -1007,7 +1007,7 @@ public class TextViewTests {
 				Assert.Equal ("This is the first line.", Clipboard.Contents);
 				Assert.Equal ("This is the first line.", Clipboard.Contents);
 				break;
 				break;
 			case 1:
 			case 1:
-				_textView.NewKeyDownEvent (new (KeyCode.DeleteChar | KeyCode.CtrlMask | KeyCode.ShiftMask));
+				_textView.NewKeyDownEvent (new (KeyCode.Delete | KeyCode.CtrlMask | KeyCode.ShiftMask));
 				Assert.Equal (0, _textView.CursorPosition.X);
 				Assert.Equal (0, _textView.CursorPosition.X);
 				Assert.Equal (0, _textView.CursorPosition.Y);
 				Assert.Equal (0, _textView.CursorPosition.Y);
 				Assert.Equal ("This is the second line.", _textView.Text);
 				Assert.Equal ("This is the second line.", _textView.Text);
@@ -1085,7 +1085,7 @@ public class TextViewTests {
 		bool iterationsFinished = false;
 		bool iterationsFinished = false;
 
 
 		while (!iterationsFinished) {
 		while (!iterationsFinished) {
-			_textView.NewKeyDownEvent (new (KeyCode.DeleteChar | KeyCode.CtrlMask));
+			_textView.NewKeyDownEvent (new (KeyCode.Delete | KeyCode.CtrlMask));
 			switch (iteration) {
 			switch (iteration) {
 			case 0:
 			case 0:
 				Assert.Equal (0, _textView.CursorPosition.X);
 				Assert.Equal (0, _textView.CursorPosition.X);
@@ -1180,7 +1180,7 @@ public class TextViewTests {
 		bool iterationsFinished = false;
 		bool iterationsFinished = false;
 
 
 		while (!iterationsFinished) {
 		while (!iterationsFinished) {
-			_textView.NewKeyDownEvent (new (KeyCode.DeleteChar | KeyCode.CtrlMask));
+			_textView.NewKeyDownEvent (new (KeyCode.Delete | KeyCode.CtrlMask));
 			switch (iteration) {
 			switch (iteration) {
 			case 0:
 			case 0:
 				Assert.Equal (0, _textView.CursorPosition.X);
 				Assert.Equal (0, _textView.CursorPosition.X);
@@ -2500,7 +2500,6 @@ line.
 	[Theory]
 	[Theory]
 	[TextViewTestsAutoInitShutdown]
 	[TextViewTestsAutoInitShutdown]
 	[InlineData (KeyCode.Delete)]
 	[InlineData (KeyCode.Delete)]
-	[InlineData (KeyCode.DeleteChar)]
 	public void WordWrap_Draw_Typed_Keys_After_Text_Is_Deleted (KeyCode del)
 	public void WordWrap_Draw_Typed_Keys_After_Text_Is_Deleted (KeyCode del)
 	{
 	{
 		Application.Top.Add (_textView);
 		Application.Top.Add (_textView);
@@ -2744,13 +2743,13 @@ Line 2.", _output);
 		Assert.Equal (0, tv.SelectedLength);
 		Assert.Equal (0, tv.SelectedLength);
 		Assert.Equal ("", tv.SelectedText);
 		Assert.Equal ("", tv.SelectedText);
 		Assert.True (tv.Selecting);
 		Assert.True (tv.Selecting);
-		Assert.True (tv.NewKeyDownEvent (new (KeyCode.DeleteChar)));
+		Assert.True (tv.NewKeyDownEvent (new (KeyCode.Delete)));
 		Assert.Equal ($"This is the first line.{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third line.first", tv.Text);
 		Assert.Equal ($"This is the first line.{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third line.first", tv.Text);
 		Assert.Equal (new Point (0, 0), tv.CursorPosition);
 		Assert.Equal (new Point (0, 0), tv.CursorPosition);
 		Assert.Equal (0, tv.SelectedLength);
 		Assert.Equal (0, tv.SelectedLength);
 		Assert.Equal ("", tv.SelectedText);
 		Assert.Equal ("", tv.SelectedText);
 		Assert.False (tv.Selecting);
 		Assert.False (tv.Selecting);
-		Assert.True (tv.NewKeyDownEvent (new (KeyCode.DeleteChar)));
+		Assert.True (tv.NewKeyDownEvent (new (KeyCode.Delete)));
 		Assert.Equal ($"his is the first line.{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third line.first", tv.Text);
 		Assert.Equal ($"his is the first line.{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third line.first", tv.Text);
 		Assert.Equal (new Point (0, 0), tv.CursorPosition);
 		Assert.Equal (new Point (0, 0), tv.CursorPosition);
 		Assert.Equal (0, tv.SelectedLength);
 		Assert.Equal (0, tv.SelectedLength);
@@ -2762,7 +2761,7 @@ Line 2.", _output);
 		Assert.True (tv.NewKeyDownEvent (new (KeyCode.End)));
 		Assert.True (tv.NewKeyDownEvent (new (KeyCode.End)));
 		Assert.Equal ($"is is the first line.{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third line.first", tv.Text);
 		Assert.Equal ($"is is the first line.{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third line.first", tv.Text);
 		Assert.Equal (new Point (21, 0), tv.CursorPosition);
 		Assert.Equal (new Point (21, 0), tv.CursorPosition);
-		Assert.True (tv.NewKeyDownEvent (new (KeyCode.Delete)));
+		Assert.True (tv.NewKeyDownEvent (new (KeyCode.Backspace)));
 		Assert.Equal ($"is is the first line{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third line.first", tv.Text);
 		Assert.Equal ($"is is the first line{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third line.first", tv.Text);
 		Assert.Equal (new Point (20, 0), tv.CursorPosition);
 		Assert.Equal (new Point (20, 0), tv.CursorPosition);
 		Assert.True (tv.NewKeyDownEvent (new (KeyCode.Backspace)));
 		Assert.True (tv.NewKeyDownEvent (new (KeyCode.Backspace)));
@@ -2808,7 +2807,7 @@ Line 2.", _output);
 		Assert.False (tv.Selecting);
 		Assert.False (tv.Selecting);
 		Assert.Equal ("is is the first lin", Clipboard.Contents);
 		Assert.Equal ("is is the first lin", Clipboard.Contents);
 		tv.CursorPosition = Point.Empty;
 		tv.CursorPosition = Point.Empty;
-		Assert.True (tv.NewKeyDownEvent (new (KeyCode.DeleteChar | KeyCode.CtrlMask | KeyCode.ShiftMask)));
+		Assert.True (tv.NewKeyDownEvent (new (KeyCode.Delete | KeyCode.CtrlMask | KeyCode.ShiftMask)));
 		Assert.Equal ($"{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third line.first", tv.Text);
 		Assert.Equal ($"{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third line.first", tv.Text);
 		Assert.Equal (new Point (0, 0), tv.CursorPosition);
 		Assert.Equal (new Point (0, 0), tv.CursorPosition);
 		Assert.Equal (0, tv.SelectedLength);
 		Assert.Equal (0, tv.SelectedLength);
@@ -2964,7 +2963,7 @@ Line 2.", _output);
 		Assert.Equal (0, tv.SelectedLength);
 		Assert.Equal (0, tv.SelectedLength);
 		Assert.Equal ("", tv.SelectedText);
 		Assert.Equal ("", tv.SelectedText);
 		Assert.False (tv.Selecting);
 		Assert.False (tv.Selecting);
-		Assert.True (tv.NewKeyDownEvent (new (KeyCode.DeleteChar | KeyCode.CtrlMask)));
+		Assert.True (tv.NewKeyDownEvent (new (KeyCode.Delete | KeyCode.CtrlMask)));
 		Assert.Equal ($"This is the second line.{Environment.NewLine}This is the third line.first", tv.Text);
 		Assert.Equal ($"This is the second line.{Environment.NewLine}This is the third line.first", tv.Text);
 		Assert.Equal (new Point (0, 0), tv.CursorPosition);
 		Assert.Equal (new Point (0, 0), tv.CursorPosition);
 		Assert.Equal (0, tv.SelectedLength);
 		Assert.Equal (0, tv.SelectedLength);
@@ -3020,7 +3019,7 @@ Line 2.", _output);
 		Assert.Equal ($"{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third ", tv.SelectedText);
 		Assert.Equal ($"{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third ", tv.SelectedText);
 		Assert.True (tv.Selecting);
 		Assert.True (tv.Selecting);
 		Assert.True (tv.Used);
 		Assert.True (tv.Used);
-		Assert.True (tv.NewKeyDownEvent (new (KeyCode.InsertChar)));
+		Assert.True (tv.NewKeyDownEvent (new (KeyCode.Insert)));
 		Assert.False (tv.Used);
 		Assert.False (tv.Used);
 		Assert.True (tv.AllowsTab);
 		Assert.True (tv.AllowsTab);
 		Assert.Equal (new Point (18, 2), tv.CursorPosition);
 		Assert.Equal (new Point (18, 2), tv.CursorPosition);
@@ -4691,7 +4690,7 @@ Line 2.", _output);
 		Assert.False (tv.IsDirty);
 		Assert.False (tv.IsDirty);
 		Assert.False (tv.HasHistoryChanges);
 		Assert.False (tv.HasHistoryChanges);
 
 
-		Assert.True (tv.NewKeyDownEvent (new (KeyCode.DeleteChar)));
+		Assert.True (tv.NewKeyDownEvent (new (KeyCode.Delete)));
 		Assert.Equal ("", tv.Text);
 		Assert.Equal ("", tv.Text);
 		Assert.Equal ("", tv.SelectedText);
 		Assert.Equal ("", tv.SelectedText);
 		Assert.Equal (1, tv.Lines);
 		Assert.Equal (1, tv.Lines);
@@ -5182,28 +5181,28 @@ Line 2.", _output);
 		var text = "First line.\nSecond line.";
 		var text = "First line.\nSecond line.";
 		var tv = new TextView () { Text = text };
 		var tv = new TextView () { Text = text };
 
 
-		Assert.True (tv.NewKeyDownEvent (new (KeyCode.DeleteChar | KeyCode.CtrlMask)));
+		Assert.True (tv.NewKeyDownEvent (new (KeyCode.Delete | KeyCode.CtrlMask)));
 		Assert.Equal ($"line.{Environment.NewLine}Second line.", tv.Text);
 		Assert.Equal ($"line.{Environment.NewLine}Second line.", tv.Text);
 		Assert.Equal ("", tv.SelectedText);
 		Assert.Equal ("", tv.SelectedText);
 		Assert.Equal (2, tv.Lines);
 		Assert.Equal (2, tv.Lines);
 		Assert.Equal (new Point (0, 0), tv.CursorPosition);
 		Assert.Equal (new Point (0, 0), tv.CursorPosition);
 
 
-		Assert.True (tv.NewKeyDownEvent (new (KeyCode.DeleteChar | KeyCode.CtrlMask)));
+		Assert.True (tv.NewKeyDownEvent (new (KeyCode.Delete | KeyCode.CtrlMask)));
 		Assert.Equal ($"{Environment.NewLine}Second line.", tv.Text);
 		Assert.Equal ($"{Environment.NewLine}Second line.", tv.Text);
 		Assert.Equal (2, tv.Lines);
 		Assert.Equal (2, tv.Lines);
 		Assert.Equal (new Point (0, 0), tv.CursorPosition);
 		Assert.Equal (new Point (0, 0), tv.CursorPosition);
 
 
-		Assert.True (tv.NewKeyDownEvent (new (KeyCode.DeleteChar | KeyCode.CtrlMask)));
+		Assert.True (tv.NewKeyDownEvent (new (KeyCode.Delete | KeyCode.CtrlMask)));
 		Assert.Equal ("Second line.", tv.Text);
 		Assert.Equal ("Second line.", tv.Text);
 		Assert.Equal (1, tv.Lines);
 		Assert.Equal (1, tv.Lines);
 		Assert.Equal (new Point (0, 0), tv.CursorPosition);
 		Assert.Equal (new Point (0, 0), tv.CursorPosition);
 
 
-		Assert.True (tv.NewKeyDownEvent (new (KeyCode.DeleteChar | KeyCode.CtrlMask)));
+		Assert.True (tv.NewKeyDownEvent (new (KeyCode.Delete | KeyCode.CtrlMask)));
 		Assert.Equal ("line.", tv.Text);
 		Assert.Equal ("line.", tv.Text);
 		Assert.Equal (1, tv.Lines);
 		Assert.Equal (1, tv.Lines);
 		Assert.Equal (new Point (0, 0), tv.CursorPosition);
 		Assert.Equal (new Point (0, 0), tv.CursorPosition);
 
 
-		Assert.True (tv.NewKeyDownEvent (new (KeyCode.DeleteChar | KeyCode.CtrlMask)));
+		Assert.True (tv.NewKeyDownEvent (new (KeyCode.Delete | KeyCode.CtrlMask)));
 		Assert.Equal ("", tv.Text);
 		Assert.Equal ("", tv.Text);
 		Assert.Equal (1, tv.Lines);
 		Assert.Equal (1, tv.Lines);
 		Assert.Equal (new Point (0, 0), tv.CursorPosition);
 		Assert.Equal (new Point (0, 0), tv.CursorPosition);
@@ -6397,7 +6396,7 @@ This is the second line.
 
 
 		tv.CursorPosition = new Point (2, 0);
 		tv.CursorPosition = new Point (2, 0);
 		Assert.Equal (new Point (2, 0), tv.CursorPosition);
 		Assert.Equal (new Point (2, 0), tv.CursorPosition);
-		Assert.True (tv.NewKeyDownEvent (new (KeyCode.DeleteChar)));
+		Assert.True (tv.NewKeyDownEvent (new (KeyCode.Delete)));
 		tv.Draw ();
 		tv.Draw ();
 		Assert.Equal (new Point (2, 0), tv.CursorPosition);
 		Assert.Equal (new Point (2, 0), tv.CursorPosition);
 		TestHelpers.AssertDriverContentsWithFrameAre (@"
 		TestHelpers.AssertDriverContentsWithFrameAre (@"
@@ -6407,7 +6406,7 @@ This is the second line.
 
 
 		tv.CursorPosition = new Point (22, 0);
 		tv.CursorPosition = new Point (22, 0);
 		Assert.Equal (new Point (22, 0), tv.CursorPosition);
 		Assert.Equal (new Point (22, 0), tv.CursorPosition);
-		Assert.True (tv.NewKeyDownEvent (new (KeyCode.DeleteChar)));
+		Assert.True (tv.NewKeyDownEvent (new (KeyCode.Delete)));
 		tv.Draw ();
 		tv.Draw ();
 		Assert.Equal (new Point (22, 0), tv.CursorPosition);
 		Assert.Equal (new Point (22, 0), tv.CursorPosition);
 		TestHelpers.AssertDriverContentsWithFrameAre (@"
 		TestHelpers.AssertDriverContentsWithFrameAre (@"
@@ -6454,7 +6453,7 @@ This is the second line.
 
 
 		tv.CursorPosition = new Point (2, 0);
 		tv.CursorPosition = new Point (2, 0);
 		Assert.Equal (new Point (2, 0), tv.CursorPosition);
 		Assert.Equal (new Point (2, 0), tv.CursorPosition);
-		Assert.True (tv.NewKeyDownEvent (new (KeyCode.DeleteChar)));
+		Assert.True (tv.NewKeyDownEvent (new (KeyCode.Delete)));
 		tv.Draw ();
 		tv.Draw ();
 		Assert.Equal (new Point (2, 0), tv.CursorPosition);
 		Assert.Equal (new Point (2, 0), tv.CursorPosition);
 		TestHelpers.AssertDriverContentsWithFrameAre (@"
 		TestHelpers.AssertDriverContentsWithFrameAre (@"
@@ -6464,7 +6463,7 @@ This is the second line.
 
 
 		tv.CursorPosition = new Point (22, 0);
 		tv.CursorPosition = new Point (22, 0);
 		Assert.Equal (new Point (22, 0), tv.CursorPosition);
 		Assert.Equal (new Point (22, 0), tv.CursorPosition);
-		Assert.True (tv.NewKeyDownEvent (new (KeyCode.DeleteChar)));
+		Assert.True (tv.NewKeyDownEvent (new (KeyCode.Delete)));
 		tv.Draw ();
 		tv.Draw ();
 		Assert.Equal (new Point (22, 0), tv.CursorPosition);
 		Assert.Equal (new Point (22, 0), tv.CursorPosition);
 		TestHelpers.AssertDriverContentsWithFrameAre (@"
 		TestHelpers.AssertDriverContentsWithFrameAre (@"

+ 1 - 1
UnitTests/Views/TimeFieldTests.cs

@@ -61,7 +61,7 @@ namespace Terminal.Gui.ViewsTests {
 		{
 		{
 			TimeField tf = new TimeField (TimeSpan.Parse ("12:12:19"));
 			TimeField tf = new TimeField (TimeSpan.Parse ("12:12:19"));
 			tf.ReadOnly = true;
 			tf.ReadOnly = true;
-			Assert.True (tf.NewKeyDownEvent (new (KeyCode.DeleteChar)));
+			Assert.True (tf.NewKeyDownEvent (new (KeyCode.Delete)));
 			Assert.Equal (" 12:12:19", tf.Text);
 			Assert.Equal (" 12:12:19", tf.Text);
 			tf.ReadOnly = false;
 			tf.ReadOnly = false;
 			Assert.True (tf.NewKeyDownEvent (new (KeyCode.D | KeyCode.CtrlMask)));
 			Assert.True (tf.NewKeyDownEvent (new (KeyCode.D | KeyCode.CtrlMask)));

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor