2
0
Эх сурвалжийг харах

Merge pull request #2032 from tznind/vkpacket

Fixes #2008 - Shift+key denied by text field as "control character" on remote systems
Tig 2 жил өмнө
parent
commit
da76bc230b

+ 38 - 12
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -617,7 +617,7 @@ namespace Terminal.Gui {
 			return keyModifiers;
 		}
 
-		void ProcessInput (Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
+		void ProcessInput ()
 		{
 			int wch;
 			var code = Curses.get_wch (out wch);
@@ -787,6 +787,8 @@ namespace Terminal.Gui {
 		}
 
 		Action<KeyEvent> keyHandler;
+		Action<KeyEvent> keyDownHandler;
+		Action<KeyEvent> keyUpHandler;
 		Action<MouseEvent> mouseHandler;
 
 		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
@@ -794,12 +796,14 @@ namespace Terminal.Gui {
 			// Note: Curses doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called
 			Curses.timeout (0);
 			this.keyHandler = keyHandler;
+			this.keyDownHandler = keyDownHandler;
+			this.keyUpHandler = keyUpHandler;
 			this.mouseHandler = mouseHandler;
 
 			var mLoop = mainLoop.Driver as UnixMainLoop;
 
 			mLoop.AddWatch (0, UnixMainLoop.Condition.PollIn, x => {
-				ProcessInput (keyHandler, keyDownHandler, keyUpHandler, mouseHandler);
+				ProcessInput ();
 				return true;
 			});
 
@@ -1128,26 +1132,48 @@ namespace Terminal.Gui {
 			return false;
 		}
 
-		public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control)
+		public override void SendKeys (char keyChar, ConsoleKey consoleKey, bool shift, bool alt, bool control)
 		{
-			Key k;
+			Key key;
 
-			if ((shift || alt || control)
-				&& keyChar - (int)Key.Space >= (uint)Key.A && keyChar - (int)Key.Space <= (uint)Key.Z) {
-				k = (Key)(keyChar - (uint)Key.Space);
+			if (consoleKey == ConsoleKey.Packet) {
+				ConsoleModifiers mod = new ConsoleModifiers ();
+				if (shift) {
+					mod |= ConsoleModifiers.Shift;
+				}
+				if (alt) {
+					mod |= ConsoleModifiers.Alt;
+				}
+				if (control) {
+					mod |= ConsoleModifiers.Control;
+				}
+				var kchar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (keyChar, mod, out uint ckey, out _);
+				key = ConsoleKeyMapping.MapConsoleKeyToKey ((ConsoleKey)ckey, out bool mappable);
+				if (mappable) {
+					key = (Key)kchar;
+				}
 			} else {
-				k = (Key)keyChar;
+				key = (Key)keyChar;
 			}
+
+			KeyModifiers km = new KeyModifiers ();
 			if (shift) {
-				k |= Key.ShiftMask;
+				if (keyChar == 0) {
+					key |= Key.ShiftMask;
+				}
+				km.Shift = shift;
 			}
 			if (alt) {
-				k |= Key.AltMask;
+				key |= Key.AltMask;
+				km.Alt = alt;
 			}
 			if (control) {
-				k |= Key.CtrlMask;
+				key |= Key.CtrlMask;
+				km.Ctrl = control;
 			}
-			keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
+			keyDownHandler (new KeyEvent (key, km));
+			keyHandler (new KeyEvent (key, km));
+			keyUpHandler (new KeyEvent (key, km));
 		}
 
 		public override bool GetColors (int value, out Color foreground, out Color background)

+ 11 - 6
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs

@@ -164,11 +164,13 @@ namespace Terminal.Gui {
 		//
 		//   T:System.IO.IOException:
 		//     An I/O error occurred.
+
+		static ConsoleColor _defaultBackgroundColor = ConsoleColor.Black;
+
 		/// <summary>
 		/// 
 		/// </summary>
 		public static ConsoleColor BackgroundColor { get; set; } = _defaultBackgroundColor;
-		static ConsoleColor _defaultBackgroundColor = ConsoleColor.Black;
 
 		//
 		// Summary:
@@ -187,11 +189,13 @@ namespace Terminal.Gui {
 		//
 		//   T:System.IO.IOException:
 		//     An I/O error occurred.
+
+		static ConsoleColor _defaultForegroundColor = ConsoleColor.Gray;
+
 		/// <summary>
 		/// 
 		/// </summary>
 		public static ConsoleColor ForegroundColor { get; set; } = _defaultForegroundColor;
-		static ConsoleColor _defaultForegroundColor = ConsoleColor.Gray;
 		//
 		// Summary:
 		//     Gets or sets the height of the buffer area.
@@ -541,6 +545,9 @@ namespace Terminal.Gui {
 		// Exceptions:
 		//   T:System.IO.IOException:
 		//     An I/O error occurred.
+
+		static char [,] _buffer = new char [WindowWidth, WindowHeight];
+
 		/// <summary>
 		/// 
 		/// </summary>
@@ -550,8 +557,6 @@ namespace Terminal.Gui {
 			SetCursorPosition (0, 0);
 		}
 
-		static char [,] _buffer = new char [WindowWidth, WindowHeight];
-
 		//
 		// Summary:
 		//     Copies a specified source area of the screen buffer to a specified destination
@@ -811,9 +816,9 @@ namespace Terminal.Gui {
 		public static ConsoleKeyInfo ReadKey (bool intercept)
 		{
 			if (MockKeyPresses.Count > 0) {
-				return MockKeyPresses.Pop();
+				return MockKeyPresses.Pop ();
 			} else {
-				return new ConsoleKeyInfo ('\0', (ConsoleKey)'\0', false,false,false);
+				return new ConsoleKeyInfo ('\0', (ConsoleKey)'\0', false, false, false);
 			}
 		}
 

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

@@ -256,6 +256,22 @@ namespace Terminal.Gui {
 			currentAttribute = c;
 		}
 
+		public ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
+		{
+			if (consoleKeyInfo.Key != ConsoleKey.Packet) {
+				return consoleKeyInfo;
+			}
+
+			var mod = consoleKeyInfo.Modifiers;
+			var shift = (mod & ConsoleModifiers.Shift) != 0;
+			var alt = (mod & ConsoleModifiers.Alt) != 0;
+			var control = (mod & ConsoleModifiers.Control) != 0;
+
+			var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out uint virtualKey, out _);
+
+			return new ConsoleKeyInfo ((char)keyChar, (ConsoleKey)virtualKey, shift, alt, control);
+		}
+
 		Key MapKey (ConsoleKeyInfo keyInfo)
 		{
 			switch (keyInfo.Key) {
@@ -263,6 +279,8 @@ namespace Terminal.Gui {
 				return MapKeyModifiers (keyInfo, Key.Esc);
 			case ConsoleKey.Tab:
 				return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab;
+			case ConsoleKey.Clear:
+				return MapKeyModifiers (keyInfo, Key.Clear);
 			case ConsoleKey.Home:
 				return MapKeyModifiers (keyInfo, Key.Home);
 			case ConsoleKey.End:
@@ -289,6 +307,8 @@ namespace Terminal.Gui {
 				return MapKeyModifiers (keyInfo, Key.DeleteChar);
 			case ConsoleKey.Insert:
 				return MapKeyModifiers (keyInfo, Key.InsertChar);
+			case ConsoleKey.PrintScreen:
+				return MapKeyModifiers (keyInfo, Key.PrintScreen);
 
 			case ConsoleKey.Oem1:
 			case ConsoleKey.Oem2:
@@ -318,6 +338,9 @@ namespace Terminal.Gui {
 				if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
 					return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta));
 				}
+				if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) {
+					return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta));
+				}
 				if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
 					if (keyInfo.KeyChar == 0) {
 						return (Key)(((uint)Key.AltMask | (uint)Key.CtrlMask) | ((uint)Key.A + delta));
@@ -335,9 +358,14 @@ namespace Terminal.Gui {
 				if (keyInfo.Modifiers == ConsoleModifiers.Control) {
 					return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta));
 				}
-				if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30) {
+				if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) {
 					return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
 				}
+				if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
+					if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30) {
+						return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
+					}
+				}
 				return (Key)((uint)keyInfo.KeyChar);
 			}
 			if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) {
@@ -387,6 +415,9 @@ namespace Terminal.Gui {
 
 		void ProcessInput (ConsoleKeyInfo consoleKey)
 		{
+			if (consoleKey.Key == ConsoleKey.Packet) {
+				consoleKey = FromVKPacketToKConsoleKeyInfo (consoleKey);
+			}
 			keyModifiers = new KeyModifiers ();
 			if (consoleKey.Modifiers.HasFlag (ConsoleModifiers.Shift)) {
 				keyModifiers.Shift = true;

+ 36 - 12
Terminal.Gui/ConsoleDrivers/NetDriver.cs

@@ -533,6 +533,7 @@ namespace Terminal.Gui {
 			int foundPoint = 0;
 			string value = "";
 			var kChar = GetKeyCharArray (cki);
+			//System.Diagnostics.Debug.WriteLine ($"kChar: {new string (kChar)}");
 			for (int i = 0; i < kChar.Length; i++) {
 				var c = kChar [i];
 				if (c == '<') {
@@ -560,6 +561,8 @@ namespace Terminal.Gui {
 					//	isButtonPressed = false;
 					//}
 
+					//System.Diagnostics.Debug.WriteLine ($"buttonCode: {buttonCode}");
+
 					switch (buttonCode) {
 					case 0:
 					case 8:
@@ -1610,6 +1613,22 @@ namespace Terminal.Gui {
 			currentAttribute = c;
 		}
 
+		public ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
+		{
+			if (consoleKeyInfo.Key != ConsoleKey.Packet) {
+				return consoleKeyInfo;
+			}
+
+			var mod = consoleKeyInfo.Modifiers;
+			var shift = (mod & ConsoleModifiers.Shift) != 0;
+			var alt = (mod & ConsoleModifiers.Alt) != 0;
+			var control = (mod & ConsoleModifiers.Control) != 0;
+
+			var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out uint virtualKey, out _);
+
+			return new ConsoleKeyInfo ((char)keyChar, (ConsoleKey)virtualKey, shift, alt, control);
+		}
+
 		Key MapKey (ConsoleKeyInfo keyInfo)
 		{
 			MapKeyModifiers (keyInfo, (Key)keyInfo.Key);
@@ -1687,7 +1706,7 @@ namespace Terminal.Gui {
 					return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta));
 				}
 				if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
-					if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30) {
+					if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) {
 						return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
 					}
 				}
@@ -1754,14 +1773,23 @@ namespace Terminal.Gui {
 		{
 			switch (inputEvent.EventType) {
 			case NetEvents.EventType.Key:
+				ConsoleKeyInfo consoleKeyInfo = inputEvent.ConsoleKeyInfo;
+				if (consoleKeyInfo.Key == ConsoleKey.Packet) {
+					consoleKeyInfo = FromVKPacketToKConsoleKeyInfo (consoleKeyInfo);
+				}
 				keyModifiers = new KeyModifiers ();
-				var map = MapKey (inputEvent.ConsoleKeyInfo);
+				var map = MapKey (consoleKeyInfo);
 				if (map == (Key)0xffffffff) {
 					return;
 				}
-				keyDownHandler (new KeyEvent (map, keyModifiers));
-				keyHandler (new KeyEvent (map, keyModifiers));
-				keyUpHandler (new KeyEvent (map, keyModifiers));
+				if (map == Key.Null) {
+					keyDownHandler (new KeyEvent (map, keyModifiers));
+					keyUpHandler (new KeyEvent (map, keyModifiers));
+				} else {
+					keyDownHandler (new KeyEvent (map, keyModifiers));
+					keyHandler (new KeyEvent (map, keyModifiers));
+					keyUpHandler (new KeyEvent (map, keyModifiers));
+				}
 				break;
 			case NetEvents.EventType.Mouse:
 				mouseHandler (ToDriverMouse (inputEvent.MouseEvent));
@@ -1804,6 +1832,8 @@ namespace Terminal.Gui {
 
 		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) {
@@ -1935,14 +1965,8 @@ namespace Terminal.Gui {
 		public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control)
 		{
 			NetEvents.InputResult input = new NetEvents.InputResult ();
-			ConsoleKey ck;
-			if (char.IsLetter (keyChar)) {
-				ck = key;
-			} else {
-				ck = (ConsoleKey)'\0';
-			}
 			input.EventType = NetEvents.EventType.Key;
-			input.ConsoleKeyInfo = new ConsoleKeyInfo (keyChar, ck, shift, alt, control);
+			input.ConsoleKeyInfo = new ConsoleKeyInfo (keyChar, key, shift, alt, control);
 
 			try {
 				ProcessInput (input);

+ 71 - 8
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -534,12 +534,14 @@ namespace Terminal.Gui {
 			public ConsoleKeyInfo consoleKeyInfo;
 			public bool CapsLock;
 			public bool NumLock;
+			public bool Scrolllock;
 
-			public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock)
+			public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock, bool scrolllock)
 			{
 				this.consoleKeyInfo = consoleKeyInfo;
 				CapsLock = capslock;
 				NumLock = numlock;
+				Scrolllock = scrolllock;
 			}
 		}
 
@@ -786,7 +788,26 @@ namespace Terminal.Gui {
 		{
 			switch (inputEvent.EventType) {
 			case WindowsConsole.EventType.Key:
+				var fromPacketKey = inputEvent.KeyEvent.wVirtualKeyCode == (uint)ConsoleKey.Packet;
+				if (fromPacketKey) {
+					inputEvent.KeyEvent = FromVKPacketToKeyEventRecord (inputEvent.KeyEvent);
+				}
 				var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent));
+				//var ke = inputEvent.KeyEvent;
+				//System.Diagnostics.Debug.WriteLine ($"fromPacketKey: {fromPacketKey}");
+				//if (ke.UnicodeChar == '\0') {
+				//	System.Diagnostics.Debug.WriteLine ("UnicodeChar: 0'\\0'");
+				//} else if (ke.UnicodeChar == 13) {
+				//	System.Diagnostics.Debug.WriteLine ("UnicodeChar: 13'\\n'");
+				//} else {
+				//	System.Diagnostics.Debug.WriteLine ($"UnicodeChar: {(uint)ke.UnicodeChar}'{ke.UnicodeChar}'");
+				//}
+				//System.Diagnostics.Debug.WriteLine ($"bKeyDown: {ke.bKeyDown}");
+				//System.Diagnostics.Debug.WriteLine ($"dwControlKeyState: {ke.dwControlKeyState}");
+				//System.Diagnostics.Debug.WriteLine ($"wRepeatCount: {ke.wRepeatCount}");
+				//System.Diagnostics.Debug.WriteLine ($"wVirtualKeyCode: {ke.wVirtualKeyCode}");
+				//System.Diagnostics.Debug.WriteLine ($"wVirtualScanCode: {ke.wVirtualScanCode}");
+
 				if (map == (Key)0xffffffff) {
 					KeyEvent key = new KeyEvent ();
 
@@ -854,6 +875,9 @@ namespace Terminal.Gui {
 						keyUpHandler (key);
 				} else {
 					if (inputEvent.KeyEvent.bKeyDown) {
+						// May occurs using SendKeys
+						if (keyModifiers == null)
+							keyModifiers = new KeyModifiers ();
 						// Key Down - Fire KeyDown Event and KeyStroke (ProcessKey) Event
 						keyDownHandler (new KeyEvent (map, keyModifiers));
 						keyHandler (new KeyEvent (map, keyModifiers));
@@ -861,7 +885,7 @@ namespace Terminal.Gui {
 						keyUpHandler (new KeyEvent (map, keyModifiers));
 					}
 				}
-				if (!inputEvent.KeyEvent.bKeyDown) {
+				if (!inputEvent.KeyEvent.bKeyDown && inputEvent.KeyEvent.dwControlKeyState == 0) {
 					keyModifiers = null;
 				}
 				break;
@@ -1242,7 +1266,38 @@ namespace Terminal.Gui {
 				keyModifiers.Scrolllock = scrolllock;
 
 			var ConsoleKeyInfo = new ConsoleKeyInfo (keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control);
-			return new WindowsConsole.ConsoleKeyInfoEx (ConsoleKeyInfo, capslock, numlock);
+
+			return new WindowsConsole.ConsoleKeyInfoEx (ConsoleKeyInfo, capslock, numlock, scrolllock);
+		}
+
+		public WindowsConsole.KeyEventRecord FromVKPacketToKeyEventRecord (WindowsConsole.KeyEventRecord keyEvent)
+		{
+			if (keyEvent.wVirtualKeyCode != (uint)ConsoleKey.Packet) {
+				return keyEvent;
+			}
+
+			var mod = new ConsoleModifiers ();
+			if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ShiftPressed)) {
+				mod |= ConsoleModifiers.Shift;
+			}
+			if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightAltPressed) ||
+				keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftAltPressed)) {
+				mod |= ConsoleModifiers.Alt;
+			}
+			if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftControlPressed) ||
+				keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightControlPressed)) {
+				mod |= ConsoleModifiers.Control;
+			}
+			var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (keyEvent.UnicodeChar, mod, out uint virtualKey, out uint scanCode);
+
+			return new WindowsConsole.KeyEventRecord {
+				UnicodeChar = (char)keyChar,
+				bKeyDown = keyEvent.bKeyDown,
+				dwControlKeyState = keyEvent.dwControlKeyState,
+				wRepeatCount = keyEvent.wRepeatCount,
+				wVirtualKeyCode = (ushort)virtualKey,
+				wVirtualScanCode = (ushort)scanCode
+			};
 		}
 
 		public Key MapKey (WindowsConsole.ConsoleKeyInfoEx keyInfoEx)
@@ -1253,6 +1308,8 @@ namespace Terminal.Gui {
 				return MapKeyModifiers (keyInfo, Key.Esc);
 			case ConsoleKey.Tab:
 				return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab;
+			case ConsoleKey.Clear:
+				return MapKeyModifiers (keyInfo, Key.Clear);
 			case ConsoleKey.Home:
 				return MapKeyModifiers (keyInfo, Key.Home);
 			case ConsoleKey.End:
@@ -1279,6 +1336,8 @@ namespace Terminal.Gui {
 				return MapKeyModifiers (keyInfo, Key.DeleteChar);
 			case ConsoleKey.Insert:
 				return MapKeyModifiers (keyInfo, Key.InsertChar);
+			case ConsoleKey.PrintScreen:
+				return MapKeyModifiers (keyInfo, Key.PrintScreen);
 
 			case ConsoleKey.NumPad0:
 				return keyInfoEx.NumLock ? Key.D0 : Key.InsertChar;
@@ -1331,6 +1390,9 @@ namespace Terminal.Gui {
 				if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
 					return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta));
 				}
+				if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) {
+					return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta));
+				}
 				if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
 					if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) {
 						return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta));
@@ -1347,8 +1409,11 @@ namespace Terminal.Gui {
 				if (keyInfo.Modifiers == ConsoleModifiers.Control) {
 					return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta));
 				}
+				if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) {
+					return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
+				}
 				if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
-					if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30) {
+					if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) {
 						return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
 					}
 				}
@@ -1369,7 +1434,7 @@ namespace Terminal.Gui {
 			return (Key)(0xffffffff);
 		}
 
-		Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
+		private Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
 		{
 			Key keyMod = new Key ();
 			if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0)
@@ -1660,9 +1725,7 @@ namespace Terminal.Gui {
 			}
 
 			keyEvent.UnicodeChar = keyChar;
-			if ((shift || alt || control)
-				&& (key >= ConsoleKey.A && key <= ConsoleKey.Z
-				|| key >= ConsoleKey.D0 && key <= ConsoleKey.D9)) {
+			if ((uint)key < 255) {
 				keyEvent.wVirtualKeyCode = (ushort)key;
 			} else {
 				keyEvent.wVirtualKeyCode = '\0';

+ 521 - 0
Terminal.Gui/Core/ConsoleKeyMapping.cs

@@ -0,0 +1,521 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+
+namespace Terminal.Gui {
+	/// <summary>
+	/// Helper class to handle the scan code and virtual key from a <see cref="ConsoleKey"/>.
+	/// </summary>
+	public static class ConsoleKeyMapping {
+		private 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;
+			}
+
+			public bool Equals (ScanCodeMapping other)
+			{
+				return (this.ScanCode.Equals (other.ScanCode) &&
+					this.VirtualKey.Equals (other.VirtualKey) &&
+					this.Modifiers.Equals (other.Modifiers) &&
+					this.UnicodeChar.Equals (other.UnicodeChar));
+			}
+		}
+
+		private static ConsoleModifiers GetModifiers (uint unicodeChar, ConsoleModifiers modifiers, bool isConsoleKey)
+		{
+			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;
+			} else if ((!isConsoleKey || (isConsoleKey && (modifiers.HasFlag (ConsoleModifiers.Shift) ||
+				modifiers.HasFlag (ConsoleModifiers.Alt) || modifiers.HasFlag (ConsoleModifiers.Control)))) &&
+				unicodeChar >= 65 && unicodeChar <= 90) {
+
+				return ConsoleModifiers.Shift;
+			}
+			return 0;
+		}
+
+		private 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;
+			}
+
+			return null;
+		}
+
+		/// <summary>
+		/// Get the <see cref="ConsoleKey"/> from a <see cref="Key"/>.
+		/// </summary>
+		/// <param name="keyValue">The key value.</param>
+		/// <param name="modifiers">The modifiers keys.</param>
+		/// <param name="scanCode">The resulting scan code.</param>
+		/// <param name="outputChar">The resulting output character.</param>
+		/// <returns>The <see cref="ConsoleKey"/> or the <paramref name="outputChar"/>.</returns>
+		public static uint GetConsoleKeyFromKey (uint keyValue, ConsoleModifiers modifiers, out uint scanCode, out uint outputChar)
+		{
+			scanCode = 0;
+			outputChar = keyValue;
+			if (keyValue == 0) {
+				return 0;
+			}
+
+			uint consoleKey = MapKeyToConsoleKey (keyValue, out bool mappable);
+			if (mappable) {
+				var mod = GetModifiers (keyValue, modifiers, false);
+				var scode = GetScanCode ("UnicodeChar", keyValue, mod);
+				if (scode != null) {
+					consoleKey = scode.VirtualKey;
+					scanCode = scode.ScanCode;
+					outputChar = scode.UnicodeChar;
+				} else {
+					consoleKey = consoleKey < 0xff ? (uint)(consoleKey & 0xff | 0xff << 8) : consoleKey;
+				}
+			} else {
+				var mod = GetModifiers (keyValue, modifiers, false);
+				var scode = GetScanCode ("VirtualKey", consoleKey, mod);
+				if (scode != null) {
+					consoleKey = scode.VirtualKey;
+					scanCode = scode.ScanCode;
+					outputChar = scode.UnicodeChar;
+				}
+			}
+
+			return consoleKey;
+		}
+
+		/// <summary>
+		/// Get the output character from the <see cref="ConsoleKey"/>.
+		/// </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>
+		public 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 (decodedChar, modifiers, true);
+			scanCode = 0;
+			var scode = unicodeChar != 0 && unicodeChar >> 8 != 0xff ? GetScanCode ("VirtualKey", decodedChar, mod) : null;
+			if (scode != null) {
+				consoleKey = scode.VirtualKey;
+				keyChar = scode.UnicodeChar;
+				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++) {
+					UnicodeCategory 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;
+						}
+					}
+				}
+			}
+
+			return keyChar;
+		}
+
+		/// <summary>
+		/// Maps a <see cref="Key"/> to a <see cref="ConsoleKey"/>.
+		/// </summary>
+		/// <param name="keyValue">The key value.</param>
+		/// <param name="isMappable">If <see langword="true"/> is mapped to a valid character, otherwise <see langword="false"/>.</param>
+		/// <returns>The <see cref="ConsoleKey"/> or the <paramref name="keyValue"/>.</returns>
+		public static uint MapKeyToConsoleKey (uint keyValue, out bool isMappable)
+		{
+			isMappable = false;
+
+			switch ((Key)keyValue) {
+			case Key.Delete:
+				return (uint)ConsoleKey.Delete;
+			case Key.CursorUp:
+				return (uint)ConsoleKey.UpArrow;
+			case Key.CursorDown:
+				return (uint)ConsoleKey.DownArrow;
+			case Key.CursorLeft:
+				return (uint)ConsoleKey.LeftArrow;
+			case Key.CursorRight:
+				return (uint)ConsoleKey.RightArrow;
+			case Key.PageUp:
+				return (uint)ConsoleKey.PageUp;
+			case Key.PageDown:
+				return (uint)ConsoleKey.PageDown;
+			case Key.Home:
+				return (uint)ConsoleKey.Home;
+			case Key.End:
+				return (uint)ConsoleKey.End;
+			case Key.InsertChar:
+				return (uint)ConsoleKey.Insert;
+			case Key.DeleteChar:
+				return (uint)ConsoleKey.Delete;
+			case Key.F1:
+				return (uint)ConsoleKey.F1;
+			case Key.F2:
+				return (uint)ConsoleKey.F2;
+			case Key.F3:
+				return (uint)ConsoleKey.F3;
+			case Key.F4:
+				return (uint)ConsoleKey.F4;
+			case Key.F5:
+				return (uint)ConsoleKey.F5;
+			case Key.F6:
+				return (uint)ConsoleKey.F6;
+			case Key.F7:
+				return (uint)ConsoleKey.F7;
+			case Key.F8:
+				return (uint)ConsoleKey.F8;
+			case Key.F9:
+				return (uint)ConsoleKey.F9;
+			case Key.F10:
+				return (uint)ConsoleKey.F10;
+			case Key.F11:
+				return (uint)ConsoleKey.F11;
+			case Key.F12:
+				return (uint)ConsoleKey.F12;
+			case Key.F13:
+				return (uint)ConsoleKey.F13;
+			case Key.F14:
+				return (uint)ConsoleKey.F14;
+			case Key.F15:
+				return (uint)ConsoleKey.F15;
+			case Key.F16:
+				return (uint)ConsoleKey.F16;
+			case Key.F17:
+				return (uint)ConsoleKey.F17;
+			case Key.F18:
+				return (uint)ConsoleKey.F18;
+			case Key.F19:
+				return (uint)ConsoleKey.F19;
+			case Key.F20:
+				return (uint)ConsoleKey.F20;
+			case Key.F21:
+				return (uint)ConsoleKey.F21;
+			case Key.F22:
+				return (uint)ConsoleKey.F22;
+			case Key.F23:
+				return (uint)ConsoleKey.F23;
+			case Key.F24:
+				return (uint)ConsoleKey.F24;
+			case Key.BackTab:
+				return (uint)ConsoleKey.Tab;
+			case Key.Unknown:
+				isMappable = true;
+				return 0;
+			}
+			isMappable = true;
+
+			return keyValue;
+		}
+
+		/// <summary>
+		/// Maps a <see cref="ConsoleKey"/> to a <see cref="Key"/>.
+		/// </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="Key"/> or the <paramref name="consoleKey"/>.</returns>
+		public static Key MapConsoleKeyToKey (ConsoleKey consoleKey, out bool isMappable)
+		{
+			isMappable = false;
+
+			switch (consoleKey) {
+			case ConsoleKey.Delete:
+				return Key.Delete;
+			case ConsoleKey.UpArrow:
+				return Key.CursorUp;
+			case ConsoleKey.DownArrow:
+				return Key.CursorDown;
+			case ConsoleKey.LeftArrow:
+				return Key.CursorLeft;
+			case ConsoleKey.RightArrow:
+				return Key.CursorRight;
+			case ConsoleKey.PageUp:
+				return Key.PageUp;
+			case ConsoleKey.PageDown:
+				return Key.PageDown;
+			case ConsoleKey.Home:
+				return Key.Home;
+			case ConsoleKey.End:
+				return Key.End;
+			case ConsoleKey.Insert:
+				return Key.InsertChar;
+			case ConsoleKey.F1:
+				return Key.F1;
+			case ConsoleKey.F2:
+				return Key.F2;
+			case ConsoleKey.F3:
+				return Key.F3;
+			case ConsoleKey.F4:
+				return Key.F4;
+			case ConsoleKey.F5:
+				return Key.F5;
+			case ConsoleKey.F6:
+				return Key.F6;
+			case ConsoleKey.F7:
+				return Key.F7;
+			case ConsoleKey.F8:
+				return Key.F8;
+			case ConsoleKey.F9:
+				return Key.F9;
+			case ConsoleKey.F10:
+				return Key.F10;
+			case ConsoleKey.F11:
+				return Key.F11;
+			case ConsoleKey.F12:
+				return Key.F12;
+			case ConsoleKey.F13:
+				return Key.F13;
+			case ConsoleKey.F14:
+				return Key.F14;
+			case ConsoleKey.F15:
+				return Key.F15;
+			case ConsoleKey.F16:
+				return Key.F16;
+			case ConsoleKey.F17:
+				return Key.F17;
+			case ConsoleKey.F18:
+				return Key.F18;
+			case ConsoleKey.F19:
+				return Key.F19;
+			case ConsoleKey.F20:
+				return Key.F20;
+			case ConsoleKey.F21:
+				return Key.F21;
+			case ConsoleKey.F22:
+				return Key.F22;
+			case ConsoleKey.F23:
+				return Key.F23;
+			case ConsoleKey.F24:
+				return Key.F24;
+			case ConsoleKey.Tab:
+				return Key.BackTab;
+			}
+			isMappable = true;
+
+			return (Key)consoleKey;
+		}
+
+		private 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 (30,65,0,97),	// A
+			new ScanCodeMapping (30,65,ConsoleModifiers.Shift,65),
+			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)
+		};
+	}
+}

+ 79 - 18
Terminal.Gui/Core/Event.cs

@@ -77,11 +77,26 @@ namespace Terminal.Gui {
 		/// </summary>
 		Null = '\0',
 
+		/// <summary>
+		/// Backspace key.
+		/// </summary>
+		Backspace = 8,
+
+		/// <summary>
+		/// The key code for the user pressing the tab key (forwards tab key).
+		/// </summary>
+		Tab = 9,
+
 		/// <summary>
 		/// The key code for the user pressing the return key.
 		/// </summary>
 		Enter = '\n',
 
+		/// <summary>
+		/// The key code for the user pressing the clear key.
+		/// </summary>
+		Clear = 12,
+
 		/// <summary>
 		/// The key code for the user pressing the escape key
 		/// </summary>
@@ -363,15 +378,10 @@ namespace Terminal.Gui {
 		/// </summary>
 		CtrlMask = 0x40000000,
 
-		/// <summary>
-		/// Backspace key.
-		/// </summary>
-		Backspace = 0x100000,
-
 		/// <summary>
 		/// Cursor up key
 		/// </summary>
-		CursorUp,
+		CursorUp = 0x100000,
 		/// <summary>
 		/// Cursor down key.
 		/// </summary>
@@ -393,21 +403,33 @@ namespace Terminal.Gui {
 		/// </summary>
 		PageDown,
 		/// <summary>
-		/// Home key
+		/// Home key.
 		/// </summary>
 		Home,
 		/// <summary>
-		/// End key
+		/// End key.
 		/// </summary>
 		End,
+
+		/// <summary>
+		/// Insert character key.
+		/// </summary>
+		InsertChar,
+
 		/// <summary>
-		/// Delete character key
+		/// Delete character key.
 		/// </summary>
 		DeleteChar,
+
 		/// <summary>
-		/// Insert character key
+		/// Shift-tab key (backwards tab key).
 		/// </summary>
-		InsertChar,
+		BackTab,
+
+		/// <summary>
+		/// Print screen character key.
+		/// </summary>
+		PrintScreen,
 
 		/// <summary>
 		/// F1 key.
@@ -457,15 +479,54 @@ namespace Terminal.Gui {
 		/// F12 key.
 		/// </summary>
 		F12,
-
 		/// <summary>
-		/// The key code for the user pressing the tab key (forwards tab key).
+		/// F13 key.
 		/// </summary>
-		Tab,
+		F13,
 		/// <summary>
-		/// Shift-tab key (backwards tab key).
+		/// F14 key.
 		/// </summary>
-		BackTab,
+		F14,
+		/// <summary>
+		/// F15 key.
+		/// </summary>
+		F15,
+		/// <summary>
+		/// F16 key.
+		/// </summary>
+		F16,
+		/// <summary>
+		/// F17 key.
+		/// </summary>
+		F17,
+		/// <summary>
+		/// F18 key.
+		/// </summary>
+		F18,
+		/// <summary>
+		/// F19 key.
+		/// </summary>
+		F19,
+		/// <summary>
+		/// F20 key.
+		/// </summary>
+		F20,
+		/// <summary>
+		/// F21 key.
+		/// </summary>
+		F21,
+		/// <summary>
+		/// F22 key.
+		/// </summary>
+		F22,
+		/// <summary>
+		/// F23 key.
+		/// </summary>
+		F23,
+		/// <summary>
+		/// F24 key.
+		/// </summary>
+		F24,
 
 		/// <summary>
 		/// A key with an unknown mapping was raised.
@@ -480,7 +541,7 @@ namespace Terminal.Gui {
 		KeyModifiers keyModifiers;
 
 		/// <summary>
-		/// Symb olid definition for the key.
+		/// Symbolic definition for the key.
 		/// </summary>
 		public Key Key;
 
@@ -573,7 +634,7 @@ namespace Terminal.Gui {
 				msg += "Scrolllock-";
 			}
 
-			msg += $"{(((uint)this.KeyValue & (uint)Key.CharMask) > 27 ? $"{(char)this.KeyValue}" : $"{key}")}";
+			msg += $"{((Key)KeyValue != Key.Unknown && ((uint)this.KeyValue & (uint)Key.CharMask) > 27 ? $"{(char)this.KeyValue}" : $"{key}")}";
 
 			return msg;
 		}

+ 18 - 0
UICatalog/Properties/launchSettings.json

@@ -26,6 +26,24 @@
     "Issue1719Repro": {
       "commandName": "Project",
       "commandLineArgs": "\"ProgressBar Styles\""
+    },
+    "VkeyPacketSimulator": {
+      "commandName": "Project",
+      "commandLineArgs": "VkeyPacketSimulator"
+    },
+    "WSL2": {
+      "commandName": "Executable",
+      "executablePath": "wsl",
+      "commandLineArgs": "dotnet UICatalog.dll"
+    },
+    "WSL2 : -usc": {
+      "commandName": "Executable",
+      "executablePath": "wsl",
+      "commandLineArgs": "dotnet UICatalog.dll -usc"
+    },
+    "WSL": {
+      "commandName": "WSL2",
+      "distributionName": ""
     }
   }
 }

+ 251 - 0
UICatalog/Scenarios/VkeyPacketSimulator.cs

@@ -0,0 +1,251 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios {
+	[ScenarioMetadata (Name: "VkeyPacketSimulator", Description: "Simulates the Virtual Key Packet")]
+	[ScenarioCategory ("Keys")]
+	public class VkeyPacketSimulator : Scenario {
+		List<int> _keyboardStrokes = new List<int> ();
+		bool _outputStarted = false;
+		bool _wasUnknown = false;
+		static ManualResetEventSlim _stopOutput = new ManualResetEventSlim (false);
+
+		public override void Setup ()
+		{
+			var label = new Label ("Input") {
+				X = Pos.Center ()
+			};
+			Win.Add (label);
+
+			var btnInput = new Button ("Select Input") {
+				X = Pos.AnchorEnd (16),
+			};
+			Win.Add (btnInput);
+
+			const string ruler = "|123456789";
+
+			var inputHorizontalRuler = new Label ("") {
+				Y = Pos.Bottom (btnInput),
+				Width = Dim.Fill (),
+				ColorScheme = Colors.Error,
+				AutoSize = false
+			};
+			Win.Add (inputHorizontalRuler);
+
+			var inputVerticalRuler = new Label ("", TextDirection.TopBottom_LeftRight) {
+				Y = Pos.Bottom (btnInput),
+				Width = 1,
+				ColorScheme = Colors.Error,
+				AutoSize = false
+			};
+			Win.Add (inputVerticalRuler);
+
+			var tvInput = new TextView {
+				X = 1,
+				Y = Pos.Bottom (inputHorizontalRuler),
+				Width = Dim.Fill (),
+				Height = Dim.Percent (50) - 1
+			};
+			Win.Add (tvInput);
+
+			label = new Label ("Output") {
+				X = Pos.Center (),
+				Y = Pos.Bottom (tvInput)
+			};
+			Win.Add (label);
+
+			var btnOutput = new Button ("Select Output") {
+				X = Pos.AnchorEnd (17),
+				Y = Pos.Top (label)
+			};
+			Win.Add (btnOutput);
+
+			var outputHorizontalRuler = new Label ("") {
+				Y = Pos.Bottom (btnOutput),
+				Width = Dim.Fill (),
+				ColorScheme = Colors.Error,
+				AutoSize = false
+			};
+			Win.Add (outputHorizontalRuler);
+
+			var outputVerticalRuler = new Label ("", TextDirection.TopBottom_LeftRight) {
+				Y = Pos.Bottom (btnOutput),
+				Width = 1,
+				Height = Dim.Fill (),
+				ColorScheme = Colors.Error,
+				AutoSize = false
+			};
+			Win.Add (outputVerticalRuler);
+
+			var tvOutput = new TextView {
+				X = 1,
+				Y = Pos.Bottom (outputHorizontalRuler),
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+				ReadOnly = true
+			};
+
+			tvOutput.KeyDown += (e) => {
+				//System.Diagnostics.Debug.WriteLine ($"Output - KeyDown: {e.KeyEvent.Key}");
+				e.Handled = true;
+				if (e.KeyEvent.Key == Key.Unknown) {
+					_wasUnknown = true;
+				}
+			};
+
+			tvOutput.KeyPress += (e) => {
+				//System.Diagnostics.Debug.WriteLine ($"Output - KeyPress - _keyboardStrokes: {_keyboardStrokes.Count}");
+				if (_outputStarted && _keyboardStrokes.Count > 0) {
+					var ev = ShortcutHelper.GetModifiersKey (e.KeyEvent);
+					//System.Diagnostics.Debug.WriteLine ($"Output - KeyPress: {ev}");
+					if (!tvOutput.ProcessKey (e.KeyEvent)) {
+						Application.MainLoop.Invoke (() => {
+							MessageBox.Query ("Keys", $"'{ShortcutHelper.GetShortcutTag (ev)}' pressed!", "Ok");
+						});
+					}
+					e.Handled = true;
+					_stopOutput.Set ();
+				}
+				//System.Diagnostics.Debug.WriteLine ($"Output - KeyPress - _keyboardStrokes: {_keyboardStrokes.Count}");
+			};
+
+			Win.Add (tvOutput);
+
+			tvInput.KeyDown += (e) => {
+				//System.Diagnostics.Debug.WriteLine ($"Input - KeyDown: {e.KeyEvent.Key}");
+				e.Handled = true;
+				if (e.KeyEvent.Key == Key.Unknown) {
+					_wasUnknown = true;
+				}
+			};
+
+			View.KeyEventEventArgs unknownChar = null;
+
+			tvInput.KeyPress += (e) => {
+				if (e.KeyEvent.Key == (Key.Q | Key.CtrlMask)) {
+					Application.RequestStop ();
+					return;
+				}
+				if (e.KeyEvent.Key == Key.Unknown) {
+					_wasUnknown = true;
+					e.Handled = true;
+					return;
+				}
+				if (_wasUnknown && _keyboardStrokes.Count == 1) {
+					_wasUnknown = false;
+				} else if (_wasUnknown && char.IsLetter ((char)e.KeyEvent.Key)) {
+					_wasUnknown = false;
+				} else if (!_wasUnknown && _keyboardStrokes.Count > 0) {
+					e.Handled = true;
+					return;
+				}
+				if (_keyboardStrokes.Count == 0) {
+					AddKeyboardStrokes (e);
+				} else {
+					_keyboardStrokes.Insert (0, 0);
+				}
+				var ev = ShortcutHelper.GetModifiersKey (e.KeyEvent);
+				//System.Diagnostics.Debug.WriteLine ($"Input - KeyPress: {ev}");
+				//System.Diagnostics.Debug.WriteLine ($"Input - KeyPress - _keyboardStrokes: {_keyboardStrokes.Count}");
+			};
+
+			tvInput.KeyUp += (e) => {
+				//System.Diagnostics.Debug.WriteLine ($"Input - KeyUp: {e.KeyEvent.Key}");
+				//var ke = e.KeyEvent;
+				var ke = ShortcutHelper.GetModifiersKey (e.KeyEvent);
+				if (_wasUnknown && (int)ke - (int)(ke & (Key.AltMask | Key.CtrlMask | Key.ShiftMask)) != 0) {
+					unknownChar = e;
+				}
+				e.Handled = true;
+				if (!_wasUnknown && _keyboardStrokes.Count > 0) {
+					_outputStarted = true;
+					tvOutput.ReadOnly = false;
+					tvOutput.SetFocus ();
+					tvOutput.SetNeedsDisplay ();
+
+					Task.Run (() => {
+						while (_outputStarted) {
+							try {
+								ConsoleModifiers mod = new ConsoleModifiers ();
+								if (ke.HasFlag (Key.ShiftMask)) {
+									mod |= ConsoleModifiers.Shift;
+								}
+								if (ke.HasFlag (Key.AltMask)) {
+									mod |= ConsoleModifiers.Alt;
+								}
+								if (ke.HasFlag (Key.CtrlMask)) {
+									mod |= ConsoleModifiers.Control;
+								}
+								for (int i = 0; i < _keyboardStrokes.Count; i++) {
+									var consoleKey = ConsoleKeyMapping.GetConsoleKeyFromKey ((uint)_keyboardStrokes [i], mod, out _, out _);
+									Application.Driver.SendKeys ((char)consoleKey, ConsoleKey.Packet, mod.HasFlag (ConsoleModifiers.Shift),
+										mod.HasFlag (ConsoleModifiers.Alt), mod.HasFlag (ConsoleModifiers.Control));
+								}
+								//}
+							} catch (Exception) {
+								Application.MainLoop.Invoke (() => {
+									MessageBox.ErrorQuery ("Error", "Couldn't send the keystrokes!", "Ok");
+									Application.RequestStop ();
+								});
+							}
+							_stopOutput.Wait ();
+							_stopOutput.Reset ();
+							_keyboardStrokes.RemoveAt (0);
+							if (_keyboardStrokes.Count == 0) {
+								_outputStarted = false;
+								Application.MainLoop.Invoke (() => {
+									tvOutput.ReadOnly = true;
+									tvInput.SetFocus ();
+								});
+							}
+						}
+						//System.Diagnostics.Debug.WriteLine ($"_outputStarted: {_outputStarted}");
+					});
+				}
+			};
+
+			btnInput.Clicked += () => {
+				if (!tvInput.HasFocus && _keyboardStrokes.Count == 0) {
+					tvInput.SetFocus ();
+				}
+			};
+
+			btnOutput.Clicked += () => {
+				if (!tvOutput.HasFocus && _keyboardStrokes.Count == 0) {
+					tvOutput.SetFocus ();
+				}
+			};
+
+			tvInput.SetFocus ();
+
+			Win.LayoutComplete += (_) => {
+				inputHorizontalRuler.Text = outputHorizontalRuler.Text = ruler.Repeat ((int)Math.Ceiling ((double)(inputHorizontalRuler.Bounds.Width) / (double)ruler.Length)) [0..(inputHorizontalRuler.Bounds.Width)];
+				inputVerticalRuler.Height = tvInput.Frame.Height + 1;
+				inputVerticalRuler.Text = ruler.Repeat ((int)Math.Ceiling ((double)(inputVerticalRuler.Bounds.Height) / (double)ruler.Length)) [0..(inputVerticalRuler.Bounds.Height)];
+				outputVerticalRuler.Text = ruler.Repeat ((int)Math.Ceiling ((double)(outputVerticalRuler.Bounds.Height) / (double)ruler.Length)) [0..(outputVerticalRuler.Bounds.Height)];
+			};
+		}
+
+		private void AddKeyboardStrokes (View.KeyEventEventArgs e)
+		{
+			var ke = e.KeyEvent;
+			var km = new KeyModifiers ();
+			if (ke.IsShift) {
+				km.Shift = true;
+			}
+			if (ke.IsAlt) {
+				km.Alt = true;
+			}
+			if (ke.IsCtrl) {
+				km.Ctrl = true;
+			}
+			var keyChar = ke.KeyValue;
+			var mK = (int)((Key)ke.KeyValue & (Key.AltMask | Key.CtrlMask | Key.ShiftMask));
+			keyChar &= ~mK;
+			_keyboardStrokes.Add (keyChar);
+		}
+	}
+}

+ 198 - 16
UnitTests/ConsoleDriverTests.cs

@@ -1,7 +1,7 @@
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
-using Terminal.Gui;
 using Terminal.Gui.Views;
 using Xunit;
 using Xunit.Abstractions;
@@ -228,7 +228,8 @@ namespace Terminal.Gui.ConsoleDrivers {
 
 			void SendKeys ()
 			{
-				var k = keyEnums [idxKey];
+				var k = shift && char.IsLetter ((char)keyEnums [idxKey]) && char.IsLower ((char)keyEnums [idxKey])
+					? (Key)char.ToUpper ((char)keyEnums [idxKey]) : keyEnums [idxKey];
 				var c = (char)k;
 				var ck = char.IsLetter (c) ? (ConsoleKey)char.ToUpper (c) : (ConsoleKey)c;
 				var mk = new KeyModifiers () {
@@ -608,29 +609,210 @@ namespace Terminal.Gui.ConsoleDrivers {
 			Application.Run (win);
 			Application.Shutdown ();
 		}
-		
+
 		[Theory]
-		[InlineData(0x0000001F, 0x241F)]
-		[InlineData(0x0000007F, 0x247F)]
-		[InlineData(0x0000009F, 0x249F)]
-		[InlineData(0x0001001A, 0x241A)]
+		[InlineData (0x0000001F, 0x241F)]
+		[InlineData (0x0000007F, 0x247F)]
+		[InlineData (0x0000009F, 0x249F)]
+		[InlineData (0x0001001A, 0x241A)]
 		public void MakePrintable_Converts_Control_Chars_To_Proper_Unicode (uint code, uint expected)
 		{
-			var actual = ConsoleDriver.MakePrintable(code);
-				
+			var actual = ConsoleDriver.MakePrintable (code);
+
 			Assert.Equal (expected, actual.Value);
 		}
-		
+
 		[Theory]
-		[InlineData(0x20)]
-		[InlineData(0x7E)]
-		[InlineData(0xA0)]
-		[InlineData(0x010020)]
+		[InlineData (0x20)]
+		[InlineData (0x7E)]
+		[InlineData (0xA0)]
+		[InlineData (0x010020)]
 		public void MakePrintable_Does_Not_Convert_Ansi_Chars_To_Unicode (uint code)
 		{
-			var actual = ConsoleDriver.MakePrintable(code);
-				
+			var actual = ConsoleDriver.MakePrintable (code);
+
 			Assert.Equal (code, actual.Value);
 		}
+
+		/// <summary>
+		/// Sometimes when using remote tools EventKeyRecord sends 'virtual keystrokes'.
+		/// These are indicated with the wVirtualKeyCode of 231. When we see this code
+		/// 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
+		/// see: https://github.com/gui-cs/Terminal.Gui/issues/2008
+		/// </summary>
+		[Theory, AutoInitShutdown]
+		[ClassData (typeof (PacketTest))]
+		public void TestVKPacket (uint unicodeCharacter, bool shift, bool alt, bool control, uint initialVirtualKey, uint initialScanCode, Key expectedRemapping, uint expectedVirtualKey, uint expectedScanCode)
+		{
+			ConsoleModifiers modifiers = new ConsoleModifiers ();
+			if (shift) {
+				modifiers |= ConsoleModifiers.Shift;
+			}
+			if (alt) {
+				modifiers |= ConsoleModifiers.Alt;
+			}
+			if (control) {
+				modifiers |= ConsoleModifiers.Control;
+			}
+			var mappedConsoleKey = ConsoleKeyMapping.GetConsoleKeyFromKey (unicodeCharacter, modifiers, out uint scanCode, out uint outputChar);
+
+			if ((scanCode > 0 || mappedConsoleKey == 0) && mappedConsoleKey == initialVirtualKey) {
+				Assert.Equal (mappedConsoleKey, initialVirtualKey);
+			} else {
+				Assert.Equal (mappedConsoleKey, outputChar < 0xff ? (uint)(outputChar & 0xff | 0xff << 8) : outputChar);
+			}
+			Assert.Equal (scanCode, initialScanCode);
+
+			var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (mappedConsoleKey, modifiers, out uint consoleKey, out scanCode);
+
+			//if (scanCode > 0 && consoleKey == keyChar && consoleKey > 48 && consoleKey > 57 && consoleKey < 65 && consoleKey > 91) {
+			if (scanCode > 0 && keyChar == 0 && consoleKey == mappedConsoleKey) {
+				Assert.Equal (0, (double)keyChar);
+			} else {
+				Assert.Equal (keyChar, unicodeCharacter);
+			}
+			Assert.Equal (consoleKey, expectedVirtualKey);
+			Assert.Equal (scanCode, expectedScanCode);
+
+			var top = Application.Top;
+
+			top.KeyPress += (e) => {
+				var after = ShortcutHelper.GetModifiersKey (e.KeyEvent);
+				Assert.Equal (expectedRemapping, after);
+				e.Handled = true;
+				Application.RequestStop ();
+			};
+
+			var iterations = -1;
+
+			Application.Iteration += () => {
+				iterations++;
+				if (iterations == 0) {
+					Application.Driver.SendKeys ((char)mappedConsoleKey, ConsoleKey.Packet, shift, alt, control);
+				}
+			};
+
+			Application.Run ();
+			Application.Shutdown ();
+		}
+
+		public class PacketTest : IEnumerable, IEnumerable<object []> {
+			public IEnumerator<object []> GetEnumerator ()
+			{
+				yield return new object [] { 'a', false, false, false, 'A', 30, Key.a, 'A', 30 };
+				yield return new object [] { 'A', true, false, false, 'A', 30, Key.A | Key.ShiftMask, 'A', 30 };
+				yield return new object [] { 'A', true, true, false, 'A', 30, Key.A | Key.ShiftMask | Key.AltMask, 'A', 30 };
+				yield return new object [] { 'A', true, true, true, 'A', 30, Key.A | Key.ShiftMask | Key.AltMask | Key.CtrlMask, 'A', 30 };
+				yield return new object [] { 'z', false, false, false, 'Z', 44, Key.z, 'Z', 44 };
+				yield return new object [] { 'Z', true, false, false, 'Z', 44, Key.Z | Key.ShiftMask, 'Z', 44 };
+				yield return new object [] { 'Z', true, true, false, 'Z', 44, Key.Z | Key.ShiftMask | Key.AltMask, 'Z', 44 };
+				yield return new object [] { 'Z', true, true, true, 'Z', 44, Key.Z | Key.ShiftMask | Key.AltMask | Key.CtrlMask, 'Z', 44 };
+				yield return new object [] { '英', false, false, false, '\0', 0, (Key)'英', '\0', 0 };
+				yield return new object [] { '英', true, false, false, '\0', 0, (Key)'英' | Key.ShiftMask, '\0', 0 };
+				yield return new object [] { '英', true, true, false, '\0', 0, (Key)'英' | Key.ShiftMask | Key.AltMask, '\0', 0 };
+				yield return new object [] { '英', true, true, true, '\0', 0, (Key)'英' | Key.ShiftMask | Key.AltMask | Key.CtrlMask, '\0', 0 };
+				yield return new object [] { '+', false, false, false, 187, 26, (Key)'+', 187, 26 };
+				yield return new object [] { '*', true, false, false, 187, 26, (Key)'*' | Key.ShiftMask, 187, 26 };
+				yield return new object [] { '+', true, true, false, 187, 26, (Key)'+' | Key.ShiftMask | Key.AltMask, 187, 26 };
+				yield return new object [] { '+', true, true, true, 187, 26, (Key)'+' | Key.ShiftMask | Key.AltMask | Key.CtrlMask, 187, 26 };
+				yield return new object [] { '1', false, false, false, '1', 2, Key.D1, '1', 2 };
+				yield return new object [] { '!', true, false, false, '1', 2, (Key)'!' | Key.ShiftMask, '1', 2 };
+				yield return new object [] { '1', true, true, false, '1', 2, Key.D1 | Key.ShiftMask | Key.AltMask, '1', 2 };
+				yield return new object [] { '1', true, true, true, '1', 2, Key.D1 | Key.ShiftMask | Key.AltMask | Key.CtrlMask, '1', 2 };
+				yield return new object [] { '1', false, true, true, '1', 2, Key.D1 | Key.AltMask | Key.CtrlMask, '1', 2 };
+				yield return new object [] { '1', false, true, true, '1', 2, Key.D1 | Key.AltMask | Key.CtrlMask, '1', 2 };
+				yield return new object [] { '2', false, false, false, '2', 3, Key.D2, '2', 3 };
+				yield return new object [] { '"', true, false, false, '2', 3, (Key)'"' | Key.ShiftMask, '2', 3 };
+				yield return new object [] { '2', true, true, false, '2', 3, Key.D2 | Key.ShiftMask | Key.AltMask, '2', 3 };
+				yield return new object [] { '2', true, true, true, '2', 3, Key.D2 | Key.ShiftMask | Key.AltMask | Key.CtrlMask, '2', 3 };
+				yield return new object [] { '@', false, true, true, '2', 3, (Key)'@' | Key.AltMask | Key.CtrlMask, '2', 3 };
+				yield return new object [] { '3', false, false, false, '3', 4, Key.D3, '3', 4 };
+				yield return new object [] { '#', true, false, false, '3', 4, (Key)'#' | Key.ShiftMask, '3', 4 };
+				yield return new object [] { '3', true, true, false, '3', 4, Key.D3 | Key.ShiftMask | Key.AltMask, '3', 4 };
+				yield return new object [] { '3', true, true, true, '3', 4, Key.D3 | Key.ShiftMask | Key.AltMask | Key.CtrlMask, '3', 4 };
+				yield return new object [] { '£', false, true, true, '3', 4, (Key)'£' | Key.AltMask | Key.CtrlMask, '3', 4 };
+				yield return new object [] { '4', false, false, false, '4', 5, Key.D4, '4', 5 };
+				yield return new object [] { '$', true, false, false, '4', 5, (Key)'$' | Key.ShiftMask, '4', 5 };
+				yield return new object [] { '4', true, true, false, '4', 5, Key.D4 | Key.ShiftMask | Key.AltMask, '4', 5 };
+				yield return new object [] { '4', true, true, true, '4', 5, Key.D4 | Key.ShiftMask | Key.AltMask | Key.CtrlMask, '4', 5 };
+				yield return new object [] { '§', false, true, true, '4', 5, (Key)'§' | Key.AltMask | Key.CtrlMask, '4', 5 };
+				yield return new object [] { '5', false, false, false, '5', 6, Key.D5, '5', 6 };
+				yield return new object [] { '%', true, false, false, '5', 6, (Key)'%' | Key.ShiftMask, '5', 6 };
+				yield return new object [] { '5', true, true, false, '5', 6, Key.D5 | Key.ShiftMask | Key.AltMask, '5', 6 };
+				yield return new object [] { '5', true, true, true, '5', 6, Key.D5 | Key.ShiftMask | Key.AltMask | Key.CtrlMask, '5', 6 };
+				yield return new object [] { '€', false, true, true, '5', 6, (Key)'€' | Key.AltMask | Key.CtrlMask, '5', 6 };
+				yield return new object [] { '6', false, false, false, '6', 7, Key.D6, '6', 7 };
+				yield return new object [] { '&', true, false, false, '6', 7, (Key)'&' | Key.ShiftMask, '6', 7 };
+				yield return new object [] { '6', true, true, false, '6', 7, Key.D6 | Key.ShiftMask | Key.AltMask, '6', 7 };
+				yield return new object [] { '6', true, true, true, '6', 7, Key.D6 | Key.ShiftMask | Key.AltMask | Key.CtrlMask, '6', 7 };
+				yield return new object [] { '6', false, true, true, '6', 7, Key.D6 | Key.AltMask | Key.CtrlMask, '6', 7 };
+				yield return new object [] { '7', false, false, false, '7', 8, Key.D7, '7', 8 };
+				yield return new object [] { '/', true, false, false, '7', 8, (Key)'/' | Key.ShiftMask, '7', 8 };
+				yield return new object [] { '7', true, true, false, '7', 8, Key.D7 | Key.ShiftMask | Key.AltMask, '7', 8 };
+				yield return new object [] { '7', true, true, true, '7', 8, Key.D7 | Key.ShiftMask | Key.AltMask | Key.CtrlMask, '7', 8 };
+				yield return new object [] { '{', false, true, true, '7', 8, (Key)'{' | Key.AltMask | Key.CtrlMask, '7', 8 };
+				yield return new object [] { '8', false, false, false, '8', 9, Key.D8, '8', 9 };
+				yield return new object [] { '(', true, false, false, '8', 9, (Key)'(' | Key.ShiftMask, '8', 9 };
+				yield return new object [] { '8', true, true, false, '8', 9, Key.D8 | Key.ShiftMask | Key.AltMask, '8', 9 };
+				yield return new object [] { '8', true, true, true, '8', 9, Key.D8 | Key.ShiftMask | Key.AltMask | Key.CtrlMask, '8', 9 };
+				yield return new object [] { '[', false, true, true, '8', 9, (Key)'[' | Key.AltMask | Key.CtrlMask, '8', 9 };
+				yield return new object [] { '9', false, false, false, '9', 10, Key.D9, '9', 10 };
+				yield return new object [] { ')', true, false, false, '9', 10, (Key)')' | Key.ShiftMask, '9', 10 };
+				yield return new object [] { '9', true, true, false, '9', 10, Key.D9 | Key.ShiftMask | Key.AltMask, '9', 10 };
+				yield return new object [] { '9', true, true, true, '9', 10, Key.D9 | Key.ShiftMask | Key.AltMask | Key.CtrlMask, '9', 10 };
+				yield return new object [] { ']', false, true, true, '9', 10, (Key)']' | Key.AltMask | Key.CtrlMask, '9', 10 };
+				yield return new object [] { '0', false, false, false, '0', 11, Key.D0, '0', 11 };
+				yield return new object [] { '=', true, false, false, '0', 11, (Key)'=' | Key.ShiftMask, '0', 11 };
+				yield return new object [] { '0', true, true, false, '0', 11, Key.D0 | Key.ShiftMask | Key.AltMask, '0', 11 };
+				yield return new object [] { '0', true, true, true, '0', 11, Key.D0 | Key.ShiftMask | Key.AltMask | Key.CtrlMask, '0', 11 };
+				yield return new object [] { '}', false, true, true, '0', 11, (Key)'}' | Key.AltMask | Key.CtrlMask, '0', 11 };
+				yield return new object [] { '\'', false, false, false, 219, 12, (Key)'\'', 219, 12 };
+				yield return new object [] { '?', true, false, false, 219, 12, (Key)'?' | Key.ShiftMask, 219, 12 };
+				yield return new object [] { '\'', true, true, false, 219, 12, (Key)'\'' | Key.ShiftMask | Key.AltMask, 219, 12 };
+				yield return new object [] { '\'', true, true, true, 219, 12, (Key)'\'' | Key.ShiftMask | Key.AltMask | Key.CtrlMask, 219, 12 };
+				yield return new object [] { '«', false, false, false, 221, 13, (Key)'«', 221, 13 };
+				yield return new object [] { '»', true, false, false, 221, 13, (Key)'»' | Key.ShiftMask, 221, 13 };
+				yield return new object [] { '«', true, true, false, 221, 13, (Key)'«' | Key.ShiftMask | Key.AltMask, 221, 13 };
+				yield return new object [] { '«', true, true, true, 221, 13, (Key)'«' | Key.ShiftMask | Key.AltMask | Key.CtrlMask, 221, 13 };
+				yield return new object [] { 'á', false, false, false, 'á', 0, (Key)'á', 'A', 30 };
+				yield return new object [] { 'Á', true, false, false, 'Á', 0, (Key)'Á' | Key.ShiftMask, 'A', 30 };
+				yield return new object [] { 'à', false, false, false, 'à', 0, (Key)'à', 'A', 30 };
+				yield return new object [] { 'À', true, false, false, 'À', 0, (Key)'À' | Key.ShiftMask, 'A', 30 };
+				yield return new object [] { 'é', false, false, false, 'é', 0, (Key)'é', 'E', 18 };
+				yield return new object [] { 'É', true, false, false, 'É', 0, (Key)'É' | Key.ShiftMask, 'E', 18 };
+				yield return new object [] { 'è', false, false, false, 'è', 0, (Key)'è', 'E', 18 };
+				yield return new object [] { 'È', true, false, false, 'È', 0, (Key)'È' | Key.ShiftMask, 'E', 18 };
+				yield return new object [] { 'í', false, false, false, 'í', 0, (Key)'í', 'I', 23 };
+				yield return new object [] { 'Í', true, false, false, 'Í', 0, (Key)'Í' | Key.ShiftMask, 'I', 23 };
+				yield return new object [] { 'ì', false, false, false, 'ì', 0, (Key)'ì', 'I', 23 };
+				yield return new object [] { 'Ì', true, false, false, 'Ì', 0, (Key)'Ì' | Key.ShiftMask, 'I', 23 };
+				yield return new object [] { 'ó', false, false, false, 'ó', 0, (Key)'ó', 'O', 24 };
+				yield return new object [] { 'Ó', true, false, false, 'Ó', 0, (Key)'Ó' | Key.ShiftMask, 'O', 24 };
+				yield return new object [] { 'ò', false, false, false, 'Ó', 0, (Key)'ò', 'O', 24 };
+				yield return new object [] { 'Ò', true, false, false, 'Ò', 0, (Key)'Ò' | Key.ShiftMask, 'O', 24 };
+				yield return new object [] { 'ú', false, false, false, 'ú', 0, (Key)'ú', 'U', 22 };
+				yield return new object [] { 'Ú', true, false, false, 'Ú', 0, (Key)'Ú' | Key.ShiftMask, 'U', 22 };
+				yield return new object [] { 'ù', false, false, false, 'ù', 0, (Key)'ù', 'U', 22 };
+				yield return new object [] { 'Ù', true, false, false, 'Ù', 0, (Key)'Ù' | Key.ShiftMask, 'U', 22 };
+				yield return new object [] { 'ö', false, false, false, 'ó', 0, (Key)'ö', 'O', 24 };
+				yield return new object [] { 'Ö', true, false, false, 'Ó', 0, (Key)'Ö' | Key.ShiftMask, 'O', 24 };
+				yield return new object [] { '<', false, false, false, 226, 86, (Key)'<', 226, 86 };
+				yield return new object [] { '>', true, false, false, 226, 86, (Key)'>' | Key.ShiftMask, 226, 86 };
+				yield return new object [] { '<', true, true, false, 226, 86, (Key)'<' | Key.ShiftMask | Key.AltMask, 226, 86 };
+				yield return new object [] { '<', true, true, true, 226, 86, (Key)'<' | Key.ShiftMask | Key.AltMask | Key.CtrlMask, 226, 86 };
+				yield return new object [] { 'ç', false, false, false, 192, 39, (Key)'ç', 192, 39 };
+				yield return new object [] { 'Ç', true, false, false, 192, 39, (Key)'Ç' | Key.ShiftMask, 192, 39 };
+				yield return new object [] { 'ç', true, true, false, 192, 39, (Key)'ç' | Key.ShiftMask | Key.AltMask, 192, 39 };
+				yield return new object [] { 'ç', true, true, true, 192, 39, (Key)'ç' | Key.ShiftMask | Key.AltMask | Key.CtrlMask, 192, 39 };
+				yield return new object [] { '¨', false, true, true, 187, 26, (Key)'¨' | Key.AltMask | Key.CtrlMask, 187, 26 };
+				yield return new object [] { (uint)Key.PageUp, false, false, false, 33, 73, Key.PageUp, 33, 73 };
+				yield return new object [] { (uint)Key.PageUp, true, false, false, 33, 73, Key.PageUp | Key.ShiftMask, 33, 73 };
+				yield return new object [] { (uint)Key.PageUp, true, true, false, 33, 73, Key.PageUp | Key.ShiftMask | Key.AltMask, 33, 73 };
+				yield return new object [] { (uint)Key.PageUp, true, true, true, 33, 73, Key.PageUp | Key.ShiftMask | Key.AltMask | Key.CtrlMask, 33, 73 };
+			}
+
+			IEnumerator IEnumerable.GetEnumerator () => GetEnumerator ();
+		}
 	}
 }