WindowsInputProcessorTests.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. using System.Collections.Concurrent;
  2. using Terminal.Gui.ConsoleDrivers;
  3. using InputRecord = Terminal.Gui.WindowsConsole.InputRecord;
  4. using ButtonState = Terminal.Gui.WindowsConsole.ButtonState;
  5. using EventFlags = Terminal.Gui.WindowsConsole.EventFlags;
  6. using ControlKeyState = Terminal.Gui.WindowsConsole.ControlKeyState;
  7. using MouseEventRecord = Terminal.Gui.WindowsConsole.MouseEventRecord;
  8. namespace UnitTests.ConsoleDrivers.V2;
  9. public class WindowsInputProcessorTests
  10. {
  11. [Fact]
  12. public void Test_ProcessQueue_CapitalHLowerE ()
  13. {
  14. ConcurrentQueue<InputRecord> queue = new ();
  15. queue.Enqueue (
  16. new()
  17. {
  18. EventType = WindowsConsole.EventType.Key,
  19. KeyEvent = new()
  20. {
  21. bKeyDown = true,
  22. UnicodeChar = 'H',
  23. dwControlKeyState = WindowsConsole.ControlKeyState.CapslockOn,
  24. wVirtualKeyCode = (ConsoleKeyMapping.VK)72,
  25. wVirtualScanCode = 35
  26. }
  27. });
  28. queue.Enqueue (
  29. new()
  30. {
  31. EventType = WindowsConsole.EventType.Key,
  32. KeyEvent = new()
  33. {
  34. bKeyDown = false,
  35. UnicodeChar = 'H',
  36. dwControlKeyState = WindowsConsole.ControlKeyState.CapslockOn,
  37. wVirtualKeyCode = (ConsoleKeyMapping.VK)72,
  38. wVirtualScanCode = 35
  39. }
  40. });
  41. queue.Enqueue (
  42. new()
  43. {
  44. EventType = WindowsConsole.EventType.Key,
  45. KeyEvent = new()
  46. {
  47. bKeyDown = true,
  48. UnicodeChar = 'i',
  49. dwControlKeyState = WindowsConsole.ControlKeyState.NoControlKeyPressed,
  50. wVirtualKeyCode = (ConsoleKeyMapping.VK)73,
  51. wVirtualScanCode = 23
  52. }
  53. });
  54. queue.Enqueue (
  55. new()
  56. {
  57. EventType = WindowsConsole.EventType.Key,
  58. KeyEvent = new()
  59. {
  60. bKeyDown = false,
  61. UnicodeChar = 'i',
  62. dwControlKeyState = WindowsConsole.ControlKeyState.NoControlKeyPressed,
  63. wVirtualKeyCode = (ConsoleKeyMapping.VK)73,
  64. wVirtualScanCode = 23
  65. }
  66. });
  67. var processor = new WindowsInputProcessor (queue);
  68. List<Key> ups = new ();
  69. List<Key> downs = new ();
  70. processor.KeyUp += (s, e) => { ups.Add (e); };
  71. processor.KeyDown += (s, e) => { downs.Add (e); };
  72. Assert.Empty (ups);
  73. Assert.Empty (downs);
  74. processor.ProcessQueue ();
  75. Assert.Equal (Key.H.WithShift, ups [0]);
  76. Assert.Equal (Key.H.WithShift, downs [0]);
  77. Assert.Equal (Key.I, ups [1]);
  78. Assert.Equal (Key.I, downs [1]);
  79. }
  80. [Fact]
  81. public void Test_ProcessQueue_Mouse_Move ()
  82. {
  83. ConcurrentQueue<InputRecord> queue = new ();
  84. queue.Enqueue (
  85. new()
  86. {
  87. EventType = WindowsConsole.EventType.Mouse,
  88. MouseEvent = new()
  89. {
  90. MousePosition = new (32, 31),
  91. ButtonState = ButtonState.NoButtonPressed,
  92. ControlKeyState = WindowsConsole.ControlKeyState.NoControlKeyPressed,
  93. EventFlags = EventFlags.MouseMoved
  94. }
  95. });
  96. var processor = new WindowsInputProcessor (queue);
  97. List<MouseEventArgs> mouseEvents = new ();
  98. processor.MouseEvent += (s, e) => { mouseEvents.Add (e); };
  99. Assert.Empty (mouseEvents);
  100. processor.ProcessQueue ();
  101. MouseEventArgs s = Assert.Single (mouseEvents);
  102. Assert.Equal (MouseFlags.ReportMousePosition, s.Flags);
  103. Assert.Equal (s.ScreenPosition, new (32, 31));
  104. }
  105. [Theory]
  106. [InlineData (ButtonState.Button1Pressed, MouseFlags.Button1Pressed)]
  107. [InlineData (ButtonState.Button2Pressed, MouseFlags.Button2Pressed)]
  108. [InlineData (ButtonState.Button3Pressed, MouseFlags.Button3Pressed)]
  109. [InlineData (ButtonState.Button4Pressed, MouseFlags.Button4Pressed)]
  110. internal void Test_ProcessQueue_Mouse_Pressed (ButtonState state, MouseFlags expectedFlag)
  111. {
  112. ConcurrentQueue<InputRecord> queue = new ();
  113. queue.Enqueue (
  114. new()
  115. {
  116. EventType = WindowsConsole.EventType.Mouse,
  117. MouseEvent = new()
  118. {
  119. MousePosition = new (32, 31),
  120. ButtonState = state,
  121. ControlKeyState = WindowsConsole.ControlKeyState.NoControlKeyPressed,
  122. EventFlags = EventFlags.MouseMoved
  123. }
  124. });
  125. var processor = new WindowsInputProcessor (queue);
  126. List<MouseEventArgs> mouseEvents = new ();
  127. processor.MouseEvent += (s, e) => { mouseEvents.Add (e); };
  128. Assert.Empty (mouseEvents);
  129. processor.ProcessQueue ();
  130. MouseEventArgs s = Assert.Single (mouseEvents);
  131. Assert.Equal (s.Flags, MouseFlags.ReportMousePosition | expectedFlag);
  132. Assert.Equal (s.ScreenPosition, new (32, 31));
  133. }
  134. [Theory]
  135. [InlineData (100, MouseFlags.WheeledUp)]
  136. [InlineData (-100, MouseFlags.WheeledDown)]
  137. internal void Test_ProcessQueue_Mouse_Wheel (int wheelValue, MouseFlags expectedFlag)
  138. {
  139. ConcurrentQueue<InputRecord> queue = new ();
  140. queue.Enqueue (
  141. new()
  142. {
  143. EventType = WindowsConsole.EventType.Mouse,
  144. MouseEvent = new()
  145. {
  146. MousePosition = new (32, 31),
  147. ButtonState = (ButtonState)wheelValue,
  148. ControlKeyState = WindowsConsole.ControlKeyState.NoControlKeyPressed,
  149. EventFlags = WindowsConsole.EventFlags.MouseWheeled
  150. }
  151. });
  152. var processor = new WindowsInputProcessor (queue);
  153. List<MouseEventArgs> mouseEvents = new ();
  154. processor.MouseEvent += (s, e) => { mouseEvents.Add (e); };
  155. Assert.Empty (mouseEvents);
  156. processor.ProcessQueue ();
  157. MouseEventArgs s = Assert.Single (mouseEvents);
  158. Assert.Equal (s.Flags, expectedFlag);
  159. Assert.Equal (s.ScreenPosition, new (32, 31));
  160. }
  161. public static IEnumerable<object []> MouseFlagTestData ()
  162. {
  163. yield return new object []
  164. {
  165. new []
  166. {
  167. Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button1Pressed),
  168. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button1Released),
  169. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
  170. }
  171. };
  172. yield return new object []
  173. {
  174. new []
  175. {
  176. Tuple.Create (ButtonState.Button2Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button2Pressed | MouseFlags.ReportMousePosition),
  177. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button2Released),
  178. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
  179. }
  180. };
  181. yield return new object []
  182. {
  183. new []
  184. {
  185. Tuple.Create (ButtonState.Button3Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition),
  186. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Released),
  187. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
  188. }
  189. };
  190. yield return new object []
  191. {
  192. new []
  193. {
  194. Tuple.Create (ButtonState.Button4Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button4Pressed | MouseFlags.ReportMousePosition),
  195. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button4Released),
  196. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.ReportMousePosition)
  197. }
  198. };
  199. yield return new object []
  200. {
  201. new []
  202. {
  203. Tuple.Create (ButtonState.RightmostButtonPressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition),
  204. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Released),
  205. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
  206. }
  207. };
  208. // Tests for holding down 2 buttons at once and releasing them one after the other
  209. yield return new object []
  210. {
  211. new []
  212. {
  213. Tuple.Create (
  214. ButtonState.Button1Pressed | ButtonState.Button2Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed,
  215. MouseFlags.Button1Pressed | MouseFlags.Button2Pressed | MouseFlags.ReportMousePosition),
  216. Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button1Pressed | MouseFlags.Button2Released),
  217. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button1Released),
  218. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
  219. }
  220. };
  221. yield return new object []
  222. {
  223. new []
  224. {
  225. Tuple.Create (
  226. ButtonState.Button3Pressed | ButtonState.Button4Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed,
  227. MouseFlags.Button3Pressed | MouseFlags.Button4Pressed | MouseFlags.ReportMousePosition),
  228. Tuple.Create (ButtonState.Button3Pressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Pressed | MouseFlags.Button4Released),
  229. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Released),
  230. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
  231. }
  232. };
  233. // Test for holding down 2 buttons at once and releasing them simultaneously
  234. yield return new object []
  235. {
  236. new []
  237. {
  238. Tuple.Create (
  239. ButtonState.Button1Pressed | ButtonState.Button2Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed,
  240. MouseFlags.Button1Pressed | MouseFlags.Button2Pressed | MouseFlags.ReportMousePosition),
  241. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button1Released | MouseFlags.Button2Released),
  242. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
  243. }
  244. };
  245. // Test that rightmost and button 3 are the same button so 2 states is still only 1 flag
  246. yield return new object []
  247. {
  248. new []
  249. {
  250. Tuple.Create (ButtonState.Button3Pressed | ButtonState.RightmostButtonPressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed,
  251. MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition),
  252. // Can swap between without raising the released
  253. Tuple.Create (ButtonState.Button3Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition),
  254. Tuple.Create (ButtonState.RightmostButtonPressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition),
  255. // Now with neither we get released
  256. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Released),
  257. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
  258. }
  259. };
  260. // Test for ControlKeyState buttons pressed and handled
  261. yield return new object []
  262. {
  263. new []
  264. {
  265. Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.LeftAltPressed, MouseFlags.Button1Pressed | MouseFlags.ButtonAlt),
  266. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.LeftAltPressed, MouseFlags.Button1Released | MouseFlags.ButtonAlt),
  267. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.LeftAltPressed, MouseFlags.None | MouseFlags.ButtonAlt)
  268. }
  269. };
  270. yield return new object []
  271. {
  272. new []
  273. {
  274. Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.RightAltPressed, MouseFlags.Button1Pressed | MouseFlags.ButtonAlt),
  275. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.RightAltPressed, MouseFlags.Button1Released | MouseFlags.ButtonAlt),
  276. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.RightAltPressed, MouseFlags.None | MouseFlags.ButtonAlt)
  277. }
  278. };
  279. yield return new object []
  280. {
  281. new []
  282. {
  283. Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.LeftControlPressed, MouseFlags.Button1Pressed | MouseFlags.ButtonCtrl),
  284. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.LeftControlPressed, MouseFlags.Button1Released | MouseFlags.ButtonCtrl),
  285. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.LeftControlPressed, MouseFlags.None | MouseFlags.ButtonCtrl)
  286. }
  287. };
  288. yield return new object []
  289. {
  290. new []
  291. {
  292. Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.RightControlPressed, MouseFlags.Button1Pressed | MouseFlags.ButtonCtrl),
  293. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.RightControlPressed, MouseFlags.Button1Released | MouseFlags.ButtonCtrl),
  294. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.RightControlPressed, MouseFlags.None | MouseFlags.ButtonCtrl)
  295. }
  296. };
  297. yield return new object []
  298. {
  299. new []
  300. {
  301. Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.ShiftPressed, MouseFlags.Button1Pressed | MouseFlags.ButtonShift),
  302. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.ShiftPressed, MouseFlags.Button1Released | MouseFlags.ButtonShift),
  303. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.ShiftPressed, MouseFlags.None | MouseFlags.ButtonShift)
  304. }
  305. };
  306. // Test for ControlKeyState buttons pressed and not handled
  307. yield return new object []
  308. {
  309. new []
  310. {
  311. Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.CapslockOn, MouseFlags.Button1Pressed),
  312. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.CapslockOn, MouseFlags.Button1Released),
  313. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.CapslockOn, MouseFlags.None)
  314. }
  315. };
  316. yield return new object []
  317. {
  318. new []
  319. {
  320. Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.EnhancedKey, MouseFlags.Button1Pressed),
  321. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.EnhancedKey, MouseFlags.Button1Released),
  322. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.EnhancedKey, MouseFlags.None)
  323. }
  324. };
  325. yield return new object []
  326. {
  327. new []
  328. {
  329. Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.NumlockOn, MouseFlags.Button1Pressed),
  330. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NumlockOn, MouseFlags.Button1Released),
  331. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NumlockOn, MouseFlags.None)
  332. }
  333. };
  334. yield return new object []
  335. {
  336. new []
  337. {
  338. Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.ScrolllockOn, MouseFlags.Button1Pressed),
  339. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.ScrolllockOn, MouseFlags.Button1Released),
  340. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.ScrolllockOn, MouseFlags.None)
  341. }
  342. };
  343. }
  344. [Theory]
  345. [MemberData (nameof (MouseFlagTestData))]
  346. internal void MouseFlags_Should_Map_Correctly (Tuple<ButtonState, EventFlags, ControlKeyState, MouseFlags> [] inputOutputPairs)
  347. {
  348. var processor = new WindowsInputProcessor (new ());
  349. foreach (Tuple<ButtonState, EventFlags, ControlKeyState, MouseFlags> pair in inputOutputPairs)
  350. {
  351. var mockEvent = new MouseEventRecord { ButtonState = pair.Item1, EventFlags = pair.Item2, ControlKeyState = pair.Item3};
  352. MouseEventArgs result = processor.ToDriverMouse (mockEvent);
  353. Assert.Equal (pair.Item4, result.Flags);
  354. }
  355. }
  356. }