ConsoleKeyMappingTests.cs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. namespace Terminal.Gui.ConsoleDriverTests;
  2. public class ConsoleKeyMappingTests
  3. {
  4. #if ENABLE_VK_PACKET_NON_WINDOWS
  5. // This test (and the GetConsoleKeyInfoFromKeyCode API) are bogus. They make no sense outside of
  6. // the context of Windows and knowing they keyboard layout. They should be removed.
  7. [Theory]
  8. [InlineData (KeyCode.A | KeyCode.ShiftMask, ConsoleKey.A, KeyCode.A, 'A')]
  9. [InlineData ((KeyCode)'a', ConsoleKey.A, (KeyCode)'a', 'a')]
  10. [InlineData ((KeyCode)'À' | KeyCode.ShiftMask, ConsoleKey.A, (KeyCode)'À', 'À')]
  11. [InlineData ((KeyCode)'à', ConsoleKey.A, (KeyCode)'à', 'à')]
  12. [InlineData ((KeyCode)'Ü' | KeyCode.ShiftMask, ConsoleKey.U, (KeyCode)'Ü', 'Ü')]
  13. [InlineData ((KeyCode)'ü', ConsoleKey.U, (KeyCode)'ü', 'ü')]
  14. [InlineData ((KeyCode)'Ý' | KeyCode.ShiftMask, ConsoleKey.Y, (KeyCode)'Ý', 'Ý')]
  15. [InlineData ((KeyCode)'ý', ConsoleKey.Y, (KeyCode)'ý', 'ý')]
  16. [InlineData ((KeyCode)'!' | KeyCode.ShiftMask, ConsoleKey.D1, (KeyCode)'!', '!')]
  17. [InlineData (KeyCode.D1 | KeyCode.ShiftMask, ConsoleKey.D1, (KeyCode)'!', '!')]
  18. [InlineData (KeyCode.D1, ConsoleKey.D1, KeyCode.D1, '1')]
  19. [InlineData (
  20. (KeyCode)'/' | KeyCode.ShiftMask,
  21. ConsoleKey.D7,
  22. (KeyCode)'/',
  23. '/'
  24. )] // BUGBUG: This is incorrect for ENG keyboards. Shift-7 should be &.
  25. [InlineData (KeyCode.D7 | KeyCode.ShiftMask, ConsoleKey.D7, (KeyCode)'/', '/')]
  26. [InlineData (KeyCode.D7, ConsoleKey.D7, KeyCode.D7, '7')]
  27. [InlineData ((KeyCode)'{' | KeyCode.AltMask | KeyCode.CtrlMask, ConsoleKey.D7, (KeyCode)'{', '{')]
  28. [InlineData ((KeyCode)'?' | KeyCode.ShiftMask, ConsoleKey.Oem4, (KeyCode)'?', '?')]
  29. [InlineData ((KeyCode)'\'', ConsoleKey.Oem4, (KeyCode)'\'', '\'')]
  30. [InlineData (KeyCode.PageDown | KeyCode.ShiftMask, ConsoleKey.PageDown, KeyCode.Null, '\0')]
  31. [InlineData (KeyCode.PageDown, ConsoleKey.PageDown, KeyCode.Null, '\0')]
  32. [InlineData ((KeyCode)'q', ConsoleKey.Q, (KeyCode)'q', 'q')]
  33. [InlineData (KeyCode.F2, ConsoleKey.F2, KeyCode.Null, '\0')]
  34. [InlineData ((KeyCode)'英', ConsoleKey.None, (KeyCode)'英', '英')]
  35. public void GetConsoleKeyInfoFromKeyCode_Tests (
  36. KeyCode keyCode,
  37. ConsoleKey expectedConsoleKey,
  38. KeyCode expectedKeyCode,
  39. char expectedKeyChar
  40. )
  41. {
  42. var consoleKeyInfo = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (keyCode);
  43. Assert.Equal (consoleKeyInfo.Key, expectedConsoleKey);
  44. Assert.Equal ((char)expectedKeyCode, expectedKeyChar);
  45. Assert.Equal (consoleKeyInfo.KeyChar, expectedKeyChar);
  46. }
  47. static object packetLock = new object ();
  48. /// <summary>
  49. /// Sometimes when using remote tools EventKeyRecord sends 'virtual keystrokes'.
  50. /// These are indicated with the wVirtualKeyCode of 231 (VK_PACKET). When we see this code
  51. /// then we need to look to the unicode character (UnicodeChar) instead of the key
  52. /// when telling the rest of the framework what button was pressed. For full details
  53. /// see: https://github.com/gui-cs/Terminal.Gui/issues/2008
  54. /// </summary>
  55. [Theory]
  56. [AutoInitShutdown]
  57. [MemberData (nameof (VKPacket))]
  58. public void TestVKPacket (
  59. uint unicodeCharacter,
  60. bool shift,
  61. bool alt,
  62. bool control,
  63. uint initialVirtualKey,
  64. uint initialScanCode,
  65. KeyCode expectedRemapping,
  66. uint expectedVirtualKey,
  67. uint expectedScanCode
  68. )
  69. {
  70. lock (packetLock)
  71. {
  72. Application._forceFakeConsole = true;
  73. Application.Init ();
  74. ConsoleKeyInfo originalConsoleKeyInfo =
  75. new ConsoleKeyInfo ((char)unicodeCharacter, (ConsoleKey)initialVirtualKey, shift, alt, control);
  76. var encodedChar = ConsoleKeyMapping.EncodeKeyCharForVKPacket (originalConsoleKeyInfo);
  77. ConsoleKeyInfo packetConsoleKeyInfo =
  78. new ConsoleKeyInfo (encodedChar, ConsoleKey.Packet, shift, alt, control);
  79. ConsoleKeyInfo consoleKeyInfo = ConsoleKeyMapping.DecodeVKPacketToKConsoleKeyInfo (packetConsoleKeyInfo);
  80. Assert.Equal (originalConsoleKeyInfo, consoleKeyInfo);
  81. var modifiers = ConsoleKeyMapping.GetModifiers (shift, alt, control);
  82. var scanCode = ConsoleKeyMapping.GetScanCodeFromConsoleKeyInfo (consoleKeyInfo);
  83. Assert.Equal ((uint)consoleKeyInfo.Key, initialVirtualKey);
  84. if (scanCode > 0 && consoleKeyInfo.KeyChar == 0)
  85. {
  86. Assert.Equal (0, (double)consoleKeyInfo.KeyChar);
  87. }
  88. else
  89. {
  90. Assert.Equal (consoleKeyInfo.KeyChar, unicodeCharacter);
  91. }
  92. Assert.Equal ((uint)consoleKeyInfo.Key, expectedVirtualKey);
  93. Assert.Equal (scanCode, initialScanCode);
  94. Assert.Equal (scanCode, expectedScanCode);
  95. var top = Application.Top;
  96. top.KeyDown += (s, e) =>
  97. {
  98. Assert.Equal (Key.ToString (expectedRemapping), Key.ToString (e.KeyCode));
  99. e.Handled = true;
  100. Application.RequestStop ();
  101. };
  102. int iterations = -1;
  103. Application.Iteration += (s, a) =>
  104. {
  105. iterations++;
  106. if (iterations == 0)
  107. {
  108. var keyChar = ConsoleKeyMapping.EncodeKeyCharForVKPacket (consoleKeyInfo);
  109. Application.Driver?.SendKeys (keyChar, ConsoleKey.Packet, shift, alt, control);
  110. }
  111. };
  112. Application.Run ();
  113. Application.Shutdown ();
  114. }
  115. }
  116. public static IEnumerable<object []> VKPacket ()
  117. {
  118. lock (packetLock)
  119. {
  120. // unicodeCharacter, shift, alt, control, initialVirtualKey, initialScanCode, expectedRemapping, expectedVirtualKey, expectedScanCode
  121. yield return new object [] { 'a', false, false, false, 'A', 30, KeyCode.A, 'A', 30 };
  122. yield return new object [] { 'A', true, false, false, 'A', 30, KeyCode.A | KeyCode.ShiftMask, 'A', 30 };
  123. yield return new object [] { 'A', true, true, false, 'A', 30, KeyCode.A | KeyCode.ShiftMask | KeyCode.AltMask, 'A', 30 };
  124. yield return new object [] { 'A', true, true, true, 'A', 30, KeyCode.A | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, 'A', 30 };
  125. yield return new object [] { 'z', false, false, false, 'Z', 44, KeyCode.Z, 'Z', 44 };
  126. yield return new object [] { 'Z', true, false, false, 'Z', 44, KeyCode.Z | KeyCode.ShiftMask, 'Z', 44 };
  127. yield return new object [] { 'Z', true, true, false, 'Z', 44, KeyCode.Z | KeyCode.ShiftMask | KeyCode.AltMask, 'Z', 44 };
  128. yield return new object [] { 'Z', true, true, true, 'Z', 44, KeyCode.Z | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, 'Z', 44 };
  129. yield return new object [] { '英', false, false, false, '\0', 0, (KeyCode)'英', '\0', 0 };
  130. yield return new object [] { '英', true, false, false, '\0', 0, (KeyCode)'英' | KeyCode.ShiftMask, '\0', 0 };
  131. yield return new object [] { '英', true, true, false, '\0', 0, (KeyCode)'英' | KeyCode.ShiftMask | KeyCode.AltMask, '\0', 0 };
  132. yield return new object [] { '英', true, true, true, '\0', 0, (KeyCode)'英' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '\0', 0 };
  133. yield return new object [] { '+', false, false, false, VK.OEM_PLUS, 26, (KeyCode)'+', VK.OEM_PLUS, 26 };
  134. yield return new object [] { '*', true, false, false, VK.OEM_PLUS, 26, (KeyCode)'*' | KeyCode.ShiftMask, VK.OEM_PLUS, 26 };
  135. yield return new object [] { '+', true, true, false, VK.OEM_PLUS, 26, (KeyCode)'+' | KeyCode.ShiftMask | KeyCode.AltMask, VK.OEM_PLUS, 26 };
  136. yield return new object [] { '+', true, true, true, VK.OEM_PLUS, 26, (KeyCode)'+' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, VK.OEM_PLUS, 26 };
  137. yield return new object [] { '1', false, false, false, '1', 2, KeyCode.D1, '1', 2 };
  138. yield return new object [] { '!', true, false, false, '1', 2, (KeyCode)'!' | KeyCode.ShiftMask, '1', 2 };
  139. yield return new object [] { '1', true, true, false, '1', 2, KeyCode.D1 | KeyCode.ShiftMask | KeyCode.AltMask, '1', 2 };
  140. yield return new object [] { '1', true, true, true, '1', 2, KeyCode.D1 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '1', 2 };
  141. yield return new object [] { '1', false, true, true, '1', 2, KeyCode.D1 | KeyCode.AltMask | KeyCode.CtrlMask, '1', 2 };
  142. yield return new object [] { '2', false, false, false, '2', 3, KeyCode.D2, '2', 3 };
  143. yield return new object [] { '"', true, false, false, '2', 3, (KeyCode)'"' | KeyCode.ShiftMask, '2', 3 };
  144. yield return new object [] { '2', true, true, false, '2', 3, KeyCode.D2 | KeyCode.ShiftMask | KeyCode.AltMask, '2', 3 };
  145. yield return new object [] { '2', true, true, true, '2', 3, KeyCode.D2 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '2', 3 };
  146. yield return new object [] { '@', false, true, true, '2', 3, (KeyCode)'@' | KeyCode.AltMask | KeyCode.CtrlMask, '2', 3 };
  147. yield return new object [] { '3', false, false, false, '3', 4, KeyCode.D3, '3', 4 };
  148. yield return new object [] { '#', true, false, false, '3', 4, (KeyCode)'#' | KeyCode.ShiftMask, '3', 4 };
  149. yield return new object [] { '3', true, true, false, '3', 4, KeyCode.D3 | KeyCode.ShiftMask | KeyCode.AltMask, '3', 4 };
  150. yield return new object [] { '3', true, true, true, '3', 4, KeyCode.D3 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '3', 4 };
  151. yield return new object [] { '£', false, true, true, '3', 4, (KeyCode)'£' | KeyCode.AltMask | KeyCode.CtrlMask, '3', 4 };
  152. yield return new object [] { '4', false, false, false, '4', 5, KeyCode.D4, '4', 5 };
  153. yield return new object [] { '$', true, false, false, '4', 5, (KeyCode)'$' | KeyCode.ShiftMask, '4', 5 };
  154. yield return new object [] { '4', true, true, false, '4', 5, KeyCode.D4 | KeyCode.ShiftMask | KeyCode.AltMask, '4', 5 };
  155. yield return new object [] { '4', true, true, true, '4', 5, KeyCode.D4 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '4', 5 };
  156. yield return new object [] { '§', false, true, true, '4', 5, (KeyCode)'§' | KeyCode.AltMask | KeyCode.CtrlMask, '4', 5 };
  157. yield return new object [] { '5', false, false, false, '5', 6, KeyCode.D5, '5', 6 };
  158. yield return new object [] { '%', true, false, false, '5', 6, (KeyCode)'%' | KeyCode.ShiftMask, '5', 6 };
  159. yield return new object [] { '5', true, true, false, '5', 6, KeyCode.D5 | KeyCode.ShiftMask | KeyCode.AltMask, '5', 6 };
  160. yield return new object [] { '5', true, true, true, '5', 6, KeyCode.D5 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '5', 6 };
  161. yield return new object [] { '€', false, true, true, '5', 6, (KeyCode)'€' | KeyCode.AltMask | KeyCode.CtrlMask, '5', 6 };
  162. yield return new object [] { '6', false, false, false, '6', 7, KeyCode.D6, '6', 7 };
  163. yield return new object [] { '&', true, false, false, '6', 7, (KeyCode)'&' | KeyCode.ShiftMask, '6', 7 };
  164. yield return new object [] { '6', true, true, false, '6', 7, KeyCode.D6 | KeyCode.ShiftMask | KeyCode.AltMask, '6', 7 };
  165. yield return new object [] { '6', true, true, true, '6', 7, KeyCode.D6 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '6', 7 };
  166. yield return new object [] { '6', false, true, true, '6', 7, KeyCode.D6 | KeyCode.AltMask | KeyCode.CtrlMask, '6', 7 };
  167. yield return new object [] { '7', false, false, false, '7', 8, KeyCode.D7, '7', 8 };
  168. yield return
  169. new object [] { '/', true, false, false, '7', 8, (KeyCode)'/' | KeyCode.ShiftMask, '7', 8 }; // BUGBUG: This is not true for ENG keyboards. Shift-7 is &.
  170. yield return new object [] { '7', true, true, false, '7', 8, KeyCode.D7 | KeyCode.ShiftMask | KeyCode.AltMask, '7', 8 };
  171. yield return new object [] { '7', true, true, true, '7', 8, KeyCode.D7 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '7', 8 };
  172. yield return new object [] { '{', false, true, true, '7', 8, (KeyCode)'{' | KeyCode.AltMask | KeyCode.CtrlMask, '7', 8 };
  173. yield return new object [] { '8', false, false, false, '8', 9, KeyCode.D8, '8', 9 };
  174. yield return new object [] { '(', true, false, false, '8', 9, (KeyCode)'(' | KeyCode.ShiftMask, '8', 9 };
  175. yield return new object [] { '8', true, true, false, '8', 9, KeyCode.D8 | KeyCode.ShiftMask | KeyCode.AltMask, '8', 9 };
  176. yield return new object [] { '8', true, true, true, '8', 9, KeyCode.D8 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '8', 9 };
  177. yield return new object [] { '[', false, true, true, '8', 9, (KeyCode)'[' | KeyCode.AltMask | KeyCode.CtrlMask, '8', 9 };
  178. yield return new object [] { '9', false, false, false, '9', 10, KeyCode.D9, '9', 10 };
  179. yield return new object [] { ')', true, false, false, '9', 10, (KeyCode)')' | KeyCode.ShiftMask, '9', 10 };
  180. yield return new object [] { '9', true, true, false, '9', 10, KeyCode.D9 | KeyCode.ShiftMask | KeyCode.AltMask, '9', 10 };
  181. yield return new object [] { '9', true, true, true, '9', 10, KeyCode.D9 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '9', 10 };
  182. yield return new object [] { ']', false, true, true, '9', 10, (KeyCode)']' | KeyCode.AltMask | KeyCode.CtrlMask, '9', 10 };
  183. yield return new object [] { '0', false, false, false, '0', 11, KeyCode.D0, '0', 11 };
  184. yield return new object [] { '=', true, false, false, '0', 11, (KeyCode)'=' | KeyCode.ShiftMask, '0', 11 };
  185. yield return new object [] { '0', true, true, false, '0', 11, KeyCode.D0 | KeyCode.ShiftMask | KeyCode.AltMask, '0', 11 };
  186. yield return new object [] { '0', true, true, true, '0', 11, KeyCode.D0 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, '0', 11 };
  187. yield return new object [] { '}', false, true, true, '0', 11, (KeyCode)'}' | KeyCode.AltMask | KeyCode.CtrlMask, '0', 11 };
  188. yield return new object [] { '\'', false, false, false, VK.OEM_4, 12, (KeyCode)'\'', VK.OEM_4, 12 };
  189. yield return new object [] { '?', true, false, false, VK.OEM_4, 12, (KeyCode)'?' | KeyCode.ShiftMask, VK.OEM_4, 12 };
  190. yield return new object [] { '\'', true, true, false, VK.OEM_4, 12, (KeyCode)'\'' | KeyCode.ShiftMask | KeyCode.AltMask, VK.OEM_4, 12 };
  191. yield return new object [] { '\'', true, true, true, VK.OEM_4, 12, (KeyCode)'\'' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, VK.OEM_4, 12 };
  192. yield return new object [] { '«', false, false, false, VK.OEM_6, 13, (KeyCode)'«', VK.OEM_6, 13 };
  193. yield return new object [] { '»', true, false, false, VK.OEM_6, 13, (KeyCode)'»' | KeyCode.ShiftMask, VK.OEM_6, 13 };
  194. yield return new object [] { '«', true, true, false, VK.OEM_6, 13, (KeyCode)'«' | KeyCode.ShiftMask | KeyCode.AltMask, VK.OEM_6, 13 };
  195. yield return new object [] { '«', true, true, true, VK.OEM_6, 13, (KeyCode)'«' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, VK.OEM_6, 13 };
  196. yield return new object [] { 'á', false, false, false, 'A', 30, (KeyCode)'á', 'A', 30 };
  197. yield return new object [] { 'Á', true, false, false, 'A', 30, (KeyCode)'Á' | KeyCode.ShiftMask, 'A', 30 };
  198. yield return new object [] { 'à', false, false, false, 'A', 30, (KeyCode)'à', 'A', 30 };
  199. yield return new object [] { 'À', true, false, false, 'A', 30, (KeyCode)'À' | KeyCode.ShiftMask, 'A', 30 };
  200. yield return new object [] { 'é', false, false, false, 'E', 18, (KeyCode)'é', 'E', 18 };
  201. yield return new object [] { 'É', true, false, false, 'E', 18, (KeyCode)'É' | KeyCode.ShiftMask, 'E', 18 };
  202. yield return new object [] { 'è', false, false, false, 'E', 18, (KeyCode)'è', 'E', 18 };
  203. yield return new object [] { 'È', true, false, false, 'E', 18, (KeyCode)'È' | KeyCode.ShiftMask, 'E', 18 };
  204. yield return new object [] { 'í', false, false, false, 'I', 23, (KeyCode)'í', 'I', 23 };
  205. yield return new object [] { 'Í', true, false, false, 'I', 23, (KeyCode)'Í' | KeyCode.ShiftMask, 'I', 23 };
  206. yield return new object [] { 'ì', false, false, false, 'I', 23, (KeyCode)'ì', 'I', 23 };
  207. yield return new object [] { 'Ì', true, false, false, 'I', 23, (KeyCode)'Ì' | KeyCode.ShiftMask, 'I', 23 };
  208. yield return new object [] { 'ó', false, false, false, 'O', 24, (KeyCode)'ó', 'O', 24 };
  209. yield return new object [] { 'Ó', true, false, false, 'O', 24, (KeyCode)'Ó' | KeyCode.ShiftMask, 'O', 24 };
  210. yield return new object [] { 'ò', false, false, false, 'O', 24, (KeyCode)'ò', 'O', 24 };
  211. yield return new object [] { 'Ò', true, false, false, 'O', 24, (KeyCode)'Ò' | KeyCode.ShiftMask, 'O', 24 };
  212. yield return new object [] { 'ú', false, false, false, 'U', 22, (KeyCode)'ú', 'U', 22 };
  213. yield return new object [] { 'Ú', true, false, false, 'U', 22, (KeyCode)'Ú' | KeyCode.ShiftMask, 'U', 22 };
  214. yield return new object [] { 'ù', false, false, false, 'U', 22, (KeyCode)'ù', 'U', 22 };
  215. yield return new object [] { 'Ù', true, false, false, 'U', 22, (KeyCode)'Ù' | KeyCode.ShiftMask, 'U', 22 };
  216. yield return new object [] { 'ö', false, false, false, 'O', 24, (KeyCode)'ö', 'O', 24 };
  217. yield return new object [] { 'Ö', true, false, false, 'O', 24, (KeyCode)'Ö' | KeyCode.ShiftMask, 'O', 24 };
  218. yield return new object [] { '<', false, false, false, VK.OEM_102, 86, (KeyCode)'<', VK.OEM_102, 86 };
  219. yield return new object [] { '>', true, false, false, VK.OEM_102, 86, (KeyCode)'>' | KeyCode.ShiftMask, VK.OEM_102, 86 };
  220. yield return new object [] { '<', true, true, false, VK.OEM_102, 86, (KeyCode)'<' | KeyCode.ShiftMask | KeyCode.AltMask, VK.OEM_102, 86 };
  221. yield return new object [] { '<', true, true, true, VK.OEM_102, 86, (KeyCode)'<' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, VK.OEM_102, 86 };
  222. yield return new object [] { 'ç', false, false, false, VK.OEM_3, 39, (KeyCode)'ç', VK.OEM_3, 39 };
  223. yield return new object [] { 'Ç', true, false, false, VK.OEM_3, 39, (KeyCode)'Ç' | KeyCode.ShiftMask, VK.OEM_3, 39 };
  224. yield return new object [] { 'ç', true, true, false, VK.OEM_3, 39, (KeyCode)'ç' | KeyCode.ShiftMask | KeyCode.AltMask, VK.OEM_3, 39 };
  225. yield return new object [] { 'ç', true, true, true, VK.OEM_3, 39, (KeyCode)'ç' | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, VK.OEM_3, 39 };
  226. yield return new object [] { '¨', false, true, true, VK.OEM_PLUS, 26, (KeyCode)'¨' | KeyCode.AltMask | KeyCode.CtrlMask, VK.OEM_PLUS, 26 };
  227. yield return new object [] { '\0', false, false, false, VK.PRIOR, 73, KeyCode.PageUp, VK.PRIOR, 73 };
  228. yield return new object [] { '\0', true, false, false, VK.PRIOR, 73, KeyCode.PageUp | KeyCode.ShiftMask, VK.PRIOR, 73 };
  229. yield return new object [] { '\0', true, true, false, VK.PRIOR, 73, KeyCode.PageUp | KeyCode.ShiftMask | KeyCode.AltMask, VK.PRIOR, 73 };
  230. yield return new object [] { '\0', true, true, true, VK.PRIOR, 73, KeyCode.PageUp | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask, VK.PRIOR, 73 };
  231. yield return new object [] { '~', false, false, false, VK.SPACE, 57, (KeyCode)'~', VK.SPACE, 57 };
  232. yield return new object [] { '^', false, false, false, VK.SPACE, 57, (KeyCode)'^', VK.SPACE, 57 };
  233. }
  234. }
  235. [Theory]
  236. [InlineData ('a', ConsoleKey.A, 'a', ConsoleKey.A)]
  237. [InlineData ('A', ConsoleKey.A, 'A', ConsoleKey.A)]
  238. [InlineData ('á', ConsoleKey.A, 'á', ConsoleKey.A)]
  239. [InlineData ('Á', ConsoleKey.A, 'Á', ConsoleKey.A)]
  240. [InlineData ('à', ConsoleKey.A, 'à', ConsoleKey.A)]
  241. [InlineData ('À', ConsoleKey.A, 'À', ConsoleKey.A)]
  242. [InlineData ('5', ConsoleKey.D5, '5', ConsoleKey.D5)]
  243. [InlineData ('%', ConsoleKey.D5, '%', ConsoleKey.D5)]
  244. [InlineData ('€', ConsoleKey.D5, '€', ConsoleKey.D5)]
  245. [InlineData ('?', ConsoleKey.Oem4, '?', ConsoleKey.Oem4)]
  246. [InlineData ('\'', ConsoleKey.Oem4, '\'', ConsoleKey.Oem4)]
  247. [InlineData ('q', ConsoleKey.Q, 'q', ConsoleKey.Q)]
  248. [InlineData ('\0', ConsoleKey.F2, '\0', ConsoleKey.F2)]
  249. [InlineData ('英', ConsoleKey.None, '英', ConsoleKey.None)]
  250. [InlineData ('´', ConsoleKey.None, '´', ConsoleKey.Oem1)]
  251. [InlineData ('`', ConsoleKey.None, '`', ConsoleKey.Oem1)]
  252. //[InlineData ('~', ConsoleKey.None, '~', ConsoleKey.Oem2)]
  253. //[InlineData ('^', ConsoleKey.None, '^', ConsoleKey.Oem2)] // BUGBUG: '^' is Shift-6 on ENG keyboard,not Oem2.
  254. // For the US standard keyboard, Oem2 is the /? key
  255. public void EncodeKeyCharForVKPacket_DecodeVKPacketToKConsoleKeyInfo (
  256. char keyChar,
  257. ConsoleKey consoleKey,
  258. char expectedChar,
  259. ConsoleKey expectedConsoleKey
  260. )
  261. {
  262. var consoleKeyInfo = new ConsoleKeyInfo (keyChar, consoleKey, false, false, false);
  263. var encodedKeyChar = ConsoleKeyMapping.EncodeKeyCharForVKPacket (consoleKeyInfo);
  264. var encodedConsoleKeyInfo = new ConsoleKeyInfo (encodedKeyChar, ConsoleKey.Packet, false, false, false);
  265. var decodedConsoleKeyInfo = ConsoleKeyMapping.DecodeVKPacketToKConsoleKeyInfo (encodedConsoleKeyInfo);
  266. Assert.Equal (consoleKeyInfo.Key, consoleKey);
  267. Assert.Equal (expectedConsoleKey, decodedConsoleKeyInfo.Key);
  268. Assert.Equal (expectedChar, decodedConsoleKeyInfo.KeyChar);
  269. }
  270. [Theory]
  271. [InlineData ((KeyCode)'a', false, ConsoleKey.A, 'a')]
  272. [InlineData (KeyCode.A | KeyCode.ShiftMask, false, ConsoleKey.A, 'A')]
  273. [InlineData ((KeyCode)'á', false, ConsoleKey.A, 'á')]
  274. [InlineData ((KeyCode)'Á' | KeyCode.ShiftMask, false, ConsoleKey.A, 'Á')]
  275. [InlineData ((KeyCode)'à', false, ConsoleKey.A, 'à')]
  276. [InlineData ((KeyCode)'À' | KeyCode.ShiftMask, false, ConsoleKey.A, 'À')]
  277. [InlineData (KeyCode.D5, false, ConsoleKey.D5, '5')]
  278. [InlineData ((KeyCode)'%' | KeyCode.ShiftMask, false, ConsoleKey.D5, '%')]
  279. //[InlineData ((KeyCode)'€' | KeyCode.AltMask | KeyCode.CtrlMask, false, ConsoleKey.D5, '€')] // Bogus test. This is not true on ENG keyboard layout.
  280. [InlineData ((KeyCode)'?' | KeyCode.ShiftMask, false, ConsoleKey.Oem4, '?')]
  281. [InlineData ((KeyCode)'\'', false, ConsoleKey.Oem4, '\'')]
  282. [InlineData ((KeyCode)'q', false, ConsoleKey.Q, 'q')]
  283. [InlineData (KeyCode.F2, true, ConsoleKey.F2, 'q')]
  284. [InlineData ((KeyCode)'英', false, ConsoleKey.None, '英')]
  285. [InlineData (KeyCode.Enter, true, ConsoleKey.Enter, '\r')]
  286. public void MapKeyCodeToConsoleKey_GetKeyCharFromUnicodeChar (
  287. KeyCode keyCode,
  288. bool expectedIsConsoleKey,
  289. ConsoleKey expectedConsoleKey,
  290. char expectedConsoleKeyChar
  291. )
  292. {
  293. var modifiers = ConsoleKeyMapping.MapToConsoleModifiers (keyCode);
  294. var consoleKey = ConsoleKeyMapping.MapKeyCodeToConsoleKey (keyCode, out bool isConsoleKey);
  295. if (isConsoleKey)
  296. {
  297. Assert.True (isConsoleKey == expectedIsConsoleKey);
  298. Assert.Equal (expectedConsoleKey, (ConsoleKey)consoleKey);
  299. Assert.Equal (expectedConsoleKeyChar, consoleKey);
  300. }
  301. else
  302. {
  303. var keyChar =
  304. ConsoleKeyMapping.GetKeyCharFromUnicodeChar (
  305. consoleKey,
  306. modifiers,
  307. out consoleKey,
  308. out _,
  309. isConsoleKey
  310. );
  311. Assert.True (isConsoleKey == expectedIsConsoleKey);
  312. Assert.Equal (expectedConsoleKey, (ConsoleKey)consoleKey);
  313. Assert.Equal (expectedConsoleKeyChar, keyChar);
  314. }
  315. }
  316. #endif
  317. [Theory]
  318. [InlineData ('a', ConsoleKey.A, false, false, false, (KeyCode)'a')]
  319. [InlineData ('A', ConsoleKey.A, true, false, false, KeyCode.A | KeyCode.ShiftMask)]
  320. [InlineData ('á', ConsoleKey.A, false, false, false, (KeyCode)'á')]
  321. [InlineData ('Á', ConsoleKey.A, true, false, false, (KeyCode)'Á' | KeyCode.ShiftMask)]
  322. [InlineData ('à', ConsoleKey.A, false, false, false, (KeyCode)'à')]
  323. [InlineData ('À', ConsoleKey.A, true, false, false, (KeyCode)'À' | KeyCode.ShiftMask)]
  324. [InlineData ('5', ConsoleKey.D5, false, false, false, KeyCode.D5)]
  325. [InlineData ('%', ConsoleKey.D5, true, false, false, (KeyCode)'%' | KeyCode.ShiftMask)]
  326. [InlineData ('€', ConsoleKey.D5, false, true, true, (KeyCode)'€' | KeyCode.AltMask | KeyCode.CtrlMask)]
  327. [InlineData ('?', ConsoleKey.Oem4, true, false, false, (KeyCode)'?' | KeyCode.ShiftMask)]
  328. [InlineData ('\'', ConsoleKey.Oem4, false, false, false, (KeyCode)'\'')]
  329. [InlineData ('q', ConsoleKey.Q, false, false, false, (KeyCode)'q')]
  330. [InlineData ('\0', ConsoleKey.F2, false, false, false, KeyCode.F2)]
  331. [InlineData ('英', ConsoleKey.None, false, false, false, (KeyCode)'英')]
  332. [InlineData ('\r', ConsoleKey.Enter, false, false, false, KeyCode.Enter)]
  333. public void MapConsoleKeyInfoToKeyCode_Also_Return_Modifiers (
  334. char keyChar,
  335. ConsoleKey consoleKey,
  336. bool shift,
  337. bool alt,
  338. bool control,
  339. KeyCode expectedKeyCode
  340. )
  341. {
  342. var consoleKeyInfo = new ConsoleKeyInfo (keyChar, consoleKey, shift, alt, control);
  343. KeyCode keyCode = ConsoleKeyMapping.MapConsoleKeyInfoToKeyCode (consoleKeyInfo);
  344. Assert.Equal (keyCode, expectedKeyCode);
  345. }
  346. [Theory]
  347. [InlineData ('a', false, false, false, (KeyCode)'a')]
  348. [InlineData ('A', true, false, false, KeyCode.A | KeyCode.ShiftMask)]
  349. [InlineData ('á', false, false, false, (KeyCode)'á')]
  350. [InlineData ('Á', true, false, false, (KeyCode)'Á' | KeyCode.ShiftMask)]
  351. [InlineData ('à', false, false, false, (KeyCode)'à')]
  352. [InlineData ('À', true, false, false, (KeyCode)'À' | KeyCode.ShiftMask)]
  353. [InlineData ('5', false, false, false, KeyCode.D5)]
  354. [InlineData ('%', true, false, false, (KeyCode)'%' | KeyCode.ShiftMask)]
  355. [InlineData ('€', false, true, true, (KeyCode)'€' | KeyCode.AltMask | KeyCode.CtrlMask)]
  356. [InlineData ('?', true, false, false, (KeyCode)'?' | KeyCode.ShiftMask)]
  357. [InlineData ('\'', false, false, false, (KeyCode)'\'')]
  358. [InlineData ('q', false, false, false, (KeyCode)'q')]
  359. [InlineData ((uint)KeyCode.F2, false, false, false, KeyCode.F2)]
  360. [InlineData ('英', false, false, false, (KeyCode)'英')]
  361. [InlineData ('\r', false, false, false, KeyCode.Enter)]
  362. [InlineData ('\n', false, false, false, (KeyCode)'\n')]
  363. public void MapToKeyCodeModifiers_Tests (
  364. uint keyChar,
  365. bool shift,
  366. bool alt,
  367. bool control,
  368. KeyCode expectedKeyCode
  369. )
  370. {
  371. ConsoleModifiers modifiers = ConsoleKeyMapping.GetModifiers (shift, alt, control);
  372. var keyCode = (KeyCode)keyChar;
  373. keyCode = ConsoleKeyMapping.MapToKeyCodeModifiers (modifiers, keyCode);
  374. Assert.Equal (keyCode, expectedKeyCode);
  375. }
  376. [Theory]
  377. [MemberData (nameof (GetScanCodeData))]
  378. public void GetScanCodeFromConsoleKeyInfo_Tests (
  379. char keyChar,
  380. ConsoleKey consoleKey,
  381. bool shift,
  382. bool alt,
  383. bool control,
  384. uint expectedScanCode
  385. )
  386. {
  387. var consoleKeyInfo = new ConsoleKeyInfo (keyChar, consoleKey, shift, alt, control);
  388. uint scanCode = ConsoleKeyMapping.GetScanCodeFromConsoleKeyInfo (consoleKeyInfo);
  389. Assert.Equal (scanCode, expectedScanCode);
  390. }
  391. public static IEnumerable<object []> GetScanCodeData ()
  392. {
  393. yield return ['a', ConsoleKey.A, false, false, false, 30];
  394. yield return ['A', ConsoleKey.A, true, false, false, 30];
  395. yield return ['á', ConsoleKey.A, false, false, false, 30];
  396. yield return ['Á', ConsoleKey.A, true, false, false, 30];
  397. yield return ['à', ConsoleKey.A, false, false, false, 30];
  398. yield return ['À', ConsoleKey.A, true, false, false, 30];
  399. yield return ['0', ConsoleKey.D0, false, false, false, 11];
  400. yield return ['=', ConsoleKey.D0, true, false, false, 11];
  401. yield return ['}', ConsoleKey.D0, false, true, true, 11];
  402. yield return ['1', ConsoleKey.D1, false, false, false, 2];
  403. yield return ['!', ConsoleKey.D1, true, false, false, 2];
  404. yield return ['2', ConsoleKey.D2, false, false, false, 3];
  405. yield return ['"', ConsoleKey.D2, true, false, false, 3];
  406. yield return ['@', ConsoleKey.D2, false, true, true, 3];
  407. yield return ['3', ConsoleKey.D3, false, false, false, 4];
  408. yield return ['#', ConsoleKey.D3, true, false, false, 4];
  409. yield return ['£', ConsoleKey.D3, false, true, true, 4];
  410. yield return ['4', ConsoleKey.D4, false, false, false, 5];
  411. yield return ['$', ConsoleKey.D4, true, false, false, 5];
  412. yield return ['§', ConsoleKey.D4, false, true, true, 5];
  413. yield return ['5', ConsoleKey.D5, false, false, false, 6];
  414. yield return ['%', ConsoleKey.D5, true, false, false, 6];
  415. yield return ['€', ConsoleKey.D5, false, true, true, 6];
  416. yield return ['6', ConsoleKey.D6, false, false, false, 7];
  417. yield return ['&', ConsoleKey.D6, true, false, false, 7];
  418. yield return ['7', ConsoleKey.D7, false, false, false, 8];
  419. yield return ['/', ConsoleKey.D7, true, false, false, 8];
  420. yield return ['{', ConsoleKey.D7, false, true, true, 8];
  421. yield return ['8', ConsoleKey.D8, false, false, false, 9];
  422. yield return ['(', ConsoleKey.D8, true, false, false, 9];
  423. yield return ['[', ConsoleKey.D8, false, true, true, 9];
  424. yield return ['9', ConsoleKey.D9, false, false, false, 10];
  425. yield return [')', ConsoleKey.D9, true, false, false, 10];
  426. yield return [']', ConsoleKey.D9, false, true, true, 10];
  427. yield return ['´', ConsoleKey.Oem1, false, false, false, 27];
  428. yield return ['`', ConsoleKey.Oem1, true, false, false, 27];
  429. yield return ['~', ConsoleKey.Oem2, false, false, false, 43];
  430. yield return ['^', ConsoleKey.Oem2, true, false, false, 43];
  431. yield return ['ç', ConsoleKey.Oem3, false, false, false, 39];
  432. yield return ['Ç', ConsoleKey.Oem3, true, false, false, 39];
  433. yield return ['\'', ConsoleKey.Oem4, false, false, false, 12];
  434. yield return ['?', ConsoleKey.Oem4, true, false, false, 12];
  435. yield return ['\\', ConsoleKey.Oem5, false, true, true, 41];
  436. yield return ['|', ConsoleKey.Oem5, true, false, false, 41];
  437. yield return ['«', ConsoleKey.Oem6, false, true, true, 13];
  438. yield return ['»', ConsoleKey.Oem6, true, false, false, 13];
  439. yield return ['º', ConsoleKey.Oem7, false, true, true, 40];
  440. yield return ['ª', ConsoleKey.Oem7, true, false, false, 40];
  441. yield return ['+', ConsoleKey.OemPlus, false, true, true, 26];
  442. yield return ['*', ConsoleKey.OemPlus, true, false, false, 26];
  443. yield return ['¨', ConsoleKey.OemPlus, false, true, true, 26];
  444. yield return [',', ConsoleKey.OemComma, false, true, true, 51];
  445. yield return [';', ConsoleKey.OemComma, true, false, false, 51];
  446. yield return ['.', ConsoleKey.OemPeriod, false, true, true, 52];
  447. yield return [':', ConsoleKey.OemPeriod, true, false, false, 52];
  448. yield return ['-', ConsoleKey.OemMinus, false, true, true, 53];
  449. yield return ['_', ConsoleKey.OemMinus, true, false, false, 53];
  450. yield return ['q', ConsoleKey.Q, false, false, false, 16];
  451. yield return ['\0', ConsoleKey.F2, false, false, false, 60];
  452. yield return ['英', ConsoleKey.None, false, false, false, 0];
  453. yield return ['英', ConsoleKey.None, true, false, false, 0];
  454. }
  455. [Theory]
  456. [MemberData (nameof (UnShiftedChars))]
  457. public void GetKeyChar_Shifted_Char_From_UnShifted_Char (
  458. char unicodeChar,
  459. char expectedKeyChar,
  460. KeyCode expectedKeyCode
  461. )
  462. {
  463. ConsoleModifiers modifiers = ConsoleKeyMapping.GetModifiers (true, false, false);
  464. uint keyChar = ConsoleKeyMapping.GetKeyChar (unicodeChar, modifiers);
  465. Assert.Equal (keyChar, expectedKeyChar);
  466. var keyCode = (KeyCode)keyChar;
  467. keyCode = ConsoleKeyMapping.MapToKeyCodeModifiers (modifiers, keyCode);
  468. Assert.Equal (keyCode, expectedKeyCode);
  469. }
  470. public static IEnumerable<object []> UnShiftedChars =>
  471. new List<object []>
  472. {
  473. new object[] { 'a', 'A', KeyCode.A | KeyCode.ShiftMask },
  474. new object[] { 'z', 'Z', KeyCode.Z | KeyCode.ShiftMask },
  475. new object[] { 'á', 'Á', (KeyCode)'Á' | KeyCode.ShiftMask },
  476. new object[] { 'à', 'À', (KeyCode)'À' | KeyCode.ShiftMask },
  477. new object[]{ 'ý', 'Ý', (KeyCode)'Ý' | KeyCode.ShiftMask },
  478. new object[]{ '1', '!', (KeyCode)'!' | KeyCode.ShiftMask },
  479. new object[]{ '2', '"', (KeyCode)'"' | KeyCode.ShiftMask },
  480. new object[]{ '3', '#', (KeyCode)'#' | KeyCode.ShiftMask },
  481. new object[]{ '4', '$', (KeyCode)'$' | KeyCode.ShiftMask },
  482. new object[]{ '5', '%', (KeyCode)'%' | KeyCode.ShiftMask },
  483. new object[]{ '6', '&', (KeyCode)'&' | KeyCode.ShiftMask },
  484. new object[]{ '7', '/', (KeyCode)'/' | KeyCode.ShiftMask },
  485. new object[]{ '8', '(', (KeyCode)'(' | KeyCode.ShiftMask },
  486. new object[]{ '9', ')', (KeyCode)')' | KeyCode.ShiftMask },
  487. new object[]{ '0', '=', (KeyCode)'=' | KeyCode.ShiftMask },
  488. new object[]{ '\\', '|', (KeyCode)'|' | KeyCode.ShiftMask },
  489. new object[]{ '\'', '?', (KeyCode)'?' | KeyCode.ShiftMask },
  490. new object[]{ '«', '»', (KeyCode)'»' | KeyCode.ShiftMask },
  491. new object[]{ '+', '*', (KeyCode)'*' | KeyCode.ShiftMask },
  492. new object[]{ '´', '`', (KeyCode)'`' | KeyCode.ShiftMask },
  493. new object[]{ 'º', 'ª', (KeyCode)'ª' | KeyCode.ShiftMask },
  494. new object[]{ '~', '^', (KeyCode)'^' | KeyCode.ShiftMask },
  495. new object[]{ '<', '>', (KeyCode)'>' | KeyCode.ShiftMask },
  496. new object[]{ ',', ';', (KeyCode)';' | KeyCode.ShiftMask },
  497. new object[]{ '.', ':', (KeyCode)':' | KeyCode.ShiftMask },
  498. new object[]{ '-', '_', (KeyCode)'_' | KeyCode.ShiftMask },
  499. };
  500. [Theory]
  501. [MemberData (nameof (ShiftedChars))]
  502. public void GetKeyChar_UnShifted_Char_From_Shifted_Char (
  503. char unicodeChar,
  504. char expectedKeyChar,
  505. KeyCode expectedKeyCode
  506. )
  507. {
  508. ConsoleModifiers modifiers = ConsoleKeyMapping.GetModifiers (false, false, false);
  509. uint keyChar = ConsoleKeyMapping.GetKeyChar (unicodeChar, modifiers);
  510. Assert.Equal (keyChar, expectedKeyChar);
  511. var keyCode = (KeyCode)keyChar;
  512. keyCode = ConsoleKeyMapping.MapToKeyCodeModifiers (modifiers, keyCode);
  513. Assert.Equal (keyCode, expectedKeyCode);
  514. }
  515. public static IEnumerable<object []> ShiftedChars =>
  516. new List<object []>
  517. {
  518. new object[] { 'A', 'a', (KeyCode)'a' },
  519. new object[] { 'Z', 'z', (KeyCode)'z' },
  520. new object[] { 'Á', 'á', (KeyCode)'á' },
  521. new object[] { 'À', 'à', (KeyCode)'à' },
  522. new object[] { 'Ý', 'ý', (KeyCode)'ý' },
  523. new object[] { '!', '1', KeyCode.D1 },
  524. new object[] { '"', '2', KeyCode.D2 },
  525. new object[] { '#', '3', KeyCode.D3 },
  526. new object[] { '$', '4', KeyCode.D4 },
  527. new object[] { '%', '5', KeyCode.D5 },
  528. new object[] { '&', '6', KeyCode.D6 },
  529. new object[] { '/', '7', KeyCode.D7 },
  530. new object[] { '(', '8', KeyCode.D8 },
  531. new object[] { ')', '9', KeyCode.D9 },
  532. new object[] { '=', '0', KeyCode.D0 },
  533. new object[] { '|', '\\', (KeyCode)'\\' },
  534. new object[] { '?', '\'', (KeyCode)'\'' },
  535. new object[] { '»', '«', (KeyCode)'«' },
  536. new object[] { '*', '+', (KeyCode)'+' },
  537. new object[] { '`', '´', (KeyCode)'´' },
  538. new object[] { 'ª', 'º', (KeyCode)'º' },
  539. new object[] { '^', '~', (KeyCode)'~' },
  540. new object[] { '>', '<', (KeyCode)'<' },
  541. new object[] { ';', ',', (KeyCode)',' },
  542. new object[] { ':', '.', (KeyCode)'.' },
  543. new object[] { '_', '-', (KeyCode)'-' }
  544. };
  545. }