WindowsInputProcessorTests.cs 19 KB

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