WindowsInputProcessorTests.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  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_Parallelizable.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 = ControlKeyState.CapslockOn,
  23. wVirtualKeyCode = (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 = ControlKeyState.CapslockOn,
  36. wVirtualKeyCode = (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 = ControlKeyState.NoControlKeyPressed,
  49. wVirtualKeyCode = (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 = ControlKeyState.NoControlKeyPressed,
  62. wVirtualKeyCode = (VK)73,
  63. wVirtualScanCode = 23
  64. }
  65. });
  66. var processor = new WindowsInputProcessor (queue);
  67. List<Key> ups = [];
  68. List<Key> downs = [];
  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 = ControlKeyState.NoControlKeyPressed,
  92. EventFlags = EventFlags.MouseMoved
  93. }
  94. });
  95. var processor = new WindowsInputProcessor (queue);
  96. List<MouseEventArgs> mouseEvents = [];
  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 = ControlKeyState.NoControlKeyPressed,
  121. EventFlags = EventFlags.MouseMoved
  122. }
  123. });
  124. var processor = new WindowsInputProcessor (queue);
  125. List<MouseEventArgs> mouseEvents = [];
  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 = ControlKeyState.NoControlKeyPressed,
  148. EventFlags = EventFlags.MouseWheeled
  149. }
  150. });
  151. var processor = new WindowsInputProcessor (queue);
  152. List<MouseEventArgs> mouseEvents = [];
  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
  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
  172. [
  173. new []
  174. {
  175. Tuple.Create (
  176. ButtonState.Button2Pressed,
  177. EventFlags.MouseMoved,
  178. ControlKeyState.NoControlKeyPressed,
  179. MouseFlags.Button2Pressed | MouseFlags.ReportMousePosition),
  180. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button2Released),
  181. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
  182. }
  183. ];
  184. yield return
  185. [
  186. new []
  187. {
  188. Tuple.Create (
  189. ButtonState.Button3Pressed,
  190. EventFlags.MouseMoved,
  191. ControlKeyState.NoControlKeyPressed,
  192. MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition),
  193. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Released),
  194. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
  195. }
  196. ];
  197. yield return
  198. [
  199. new []
  200. {
  201. Tuple.Create (
  202. ButtonState.Button4Pressed,
  203. EventFlags.MouseMoved,
  204. ControlKeyState.NoControlKeyPressed,
  205. MouseFlags.Button4Pressed | MouseFlags.ReportMousePosition),
  206. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button4Released),
  207. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.ReportMousePosition)
  208. }
  209. ];
  210. yield return
  211. [
  212. new []
  213. {
  214. Tuple.Create (
  215. ButtonState.RightmostButtonPressed,
  216. EventFlags.MouseMoved,
  217. ControlKeyState.NoControlKeyPressed,
  218. MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition),
  219. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Released),
  220. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
  221. }
  222. ];
  223. // Tests for holding down 2 buttons at once and releasing them one after the other
  224. yield return
  225. [
  226. new []
  227. {
  228. Tuple.Create (
  229. ButtonState.Button1Pressed | ButtonState.Button2Pressed,
  230. EventFlags.MouseMoved,
  231. ControlKeyState.NoControlKeyPressed,
  232. MouseFlags.Button1Pressed | MouseFlags.Button2Pressed | MouseFlags.ReportMousePosition),
  233. Tuple.Create (
  234. ButtonState.Button1Pressed,
  235. EventFlags.NoEvent,
  236. ControlKeyState.NoControlKeyPressed,
  237. MouseFlags.Button1Pressed | MouseFlags.Button2Released),
  238. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button1Released),
  239. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
  240. }
  241. ];
  242. yield return
  243. [
  244. new []
  245. {
  246. Tuple.Create (
  247. ButtonState.Button3Pressed | ButtonState.Button4Pressed,
  248. EventFlags.MouseMoved,
  249. ControlKeyState.NoControlKeyPressed,
  250. MouseFlags.Button3Pressed | MouseFlags.Button4Pressed | MouseFlags.ReportMousePosition),
  251. Tuple.Create (
  252. ButtonState.Button3Pressed,
  253. EventFlags.NoEvent,
  254. ControlKeyState.NoControlKeyPressed,
  255. MouseFlags.Button3Pressed | MouseFlags.Button4Released),
  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 holding down 2 buttons at once and releasing them simultaneously
  261. yield return
  262. [
  263. new []
  264. {
  265. Tuple.Create (
  266. ButtonState.Button1Pressed | ButtonState.Button2Pressed,
  267. EventFlags.MouseMoved,
  268. ControlKeyState.NoControlKeyPressed,
  269. MouseFlags.Button1Pressed | MouseFlags.Button2Pressed | MouseFlags.ReportMousePosition),
  270. Tuple.Create (
  271. ButtonState.NoButtonPressed,
  272. EventFlags.NoEvent,
  273. ControlKeyState.NoControlKeyPressed,
  274. MouseFlags.Button1Released | MouseFlags.Button2Released),
  275. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
  276. }
  277. ];
  278. // Test that rightmost and button 3 are the same button so 2 states is still only 1 flag
  279. yield return
  280. [
  281. new []
  282. {
  283. Tuple.Create (
  284. ButtonState.Button3Pressed | ButtonState.RightmostButtonPressed,
  285. EventFlags.MouseMoved,
  286. ControlKeyState.NoControlKeyPressed,
  287. MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition),
  288. // Can swap between without raising the released
  289. Tuple.Create (
  290. ButtonState.Button3Pressed,
  291. EventFlags.MouseMoved,
  292. ControlKeyState.NoControlKeyPressed,
  293. MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition),
  294. Tuple.Create (
  295. ButtonState.RightmostButtonPressed,
  296. EventFlags.MouseMoved,
  297. ControlKeyState.NoControlKeyPressed,
  298. MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition),
  299. // Now with neither we get released
  300. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Released),
  301. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None)
  302. }
  303. ];
  304. // Test for ControlKeyState buttons pressed and handled
  305. yield return
  306. [
  307. new []
  308. {
  309. Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.LeftAltPressed, MouseFlags.Button1Pressed | MouseFlags.ButtonAlt),
  310. Tuple.Create (
  311. ButtonState.NoButtonPressed,
  312. EventFlags.NoEvent,
  313. ControlKeyState.LeftAltPressed,
  314. MouseFlags.Button1Released | MouseFlags.ButtonAlt),
  315. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.LeftAltPressed, MouseFlags.None | MouseFlags.ButtonAlt)
  316. }
  317. ];
  318. yield return
  319. [
  320. new []
  321. {
  322. Tuple.Create (
  323. ButtonState.Button1Pressed,
  324. EventFlags.NoEvent,
  325. ControlKeyState.RightAltPressed,
  326. MouseFlags.Button1Pressed | MouseFlags.ButtonAlt),
  327. Tuple.Create (
  328. ButtonState.NoButtonPressed,
  329. EventFlags.NoEvent,
  330. ControlKeyState.RightAltPressed,
  331. MouseFlags.Button1Released | MouseFlags.ButtonAlt),
  332. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.RightAltPressed, MouseFlags.None | MouseFlags.ButtonAlt)
  333. }
  334. ];
  335. yield return
  336. [
  337. new []
  338. {
  339. Tuple.Create (
  340. ButtonState.Button1Pressed,
  341. EventFlags.NoEvent,
  342. ControlKeyState.LeftControlPressed,
  343. MouseFlags.Button1Pressed | MouseFlags.ButtonCtrl),
  344. Tuple.Create (
  345. ButtonState.NoButtonPressed,
  346. EventFlags.NoEvent,
  347. ControlKeyState.LeftControlPressed,
  348. MouseFlags.Button1Released | MouseFlags.ButtonCtrl),
  349. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.LeftControlPressed, MouseFlags.None | MouseFlags.ButtonCtrl)
  350. }
  351. ];
  352. yield return
  353. [
  354. new []
  355. {
  356. Tuple.Create (
  357. ButtonState.Button1Pressed,
  358. EventFlags.NoEvent,
  359. ControlKeyState.RightControlPressed,
  360. MouseFlags.Button1Pressed | MouseFlags.ButtonCtrl),
  361. Tuple.Create (
  362. ButtonState.NoButtonPressed,
  363. EventFlags.NoEvent,
  364. ControlKeyState.RightControlPressed,
  365. MouseFlags.Button1Released | MouseFlags.ButtonCtrl),
  366. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.RightControlPressed, MouseFlags.None | MouseFlags.ButtonCtrl)
  367. }
  368. ];
  369. yield return
  370. [
  371. new []
  372. {
  373. Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.ShiftPressed, MouseFlags.Button1Pressed | MouseFlags.ButtonShift),
  374. Tuple.Create (
  375. ButtonState.NoButtonPressed,
  376. EventFlags.NoEvent,
  377. ControlKeyState.ShiftPressed,
  378. MouseFlags.Button1Released | MouseFlags.ButtonShift),
  379. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.ShiftPressed, MouseFlags.None | MouseFlags.ButtonShift)
  380. }
  381. ];
  382. // Test for ControlKeyState buttons pressed and not handled
  383. yield return
  384. [
  385. new []
  386. {
  387. Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.CapslockOn, MouseFlags.Button1Pressed),
  388. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.CapslockOn, MouseFlags.Button1Released),
  389. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.CapslockOn, MouseFlags.None)
  390. }
  391. ];
  392. yield return
  393. [
  394. new []
  395. {
  396. Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.EnhancedKey, MouseFlags.Button1Pressed),
  397. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.EnhancedKey, MouseFlags.Button1Released),
  398. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.EnhancedKey, MouseFlags.None)
  399. }
  400. ];
  401. yield return
  402. [
  403. new []
  404. {
  405. Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.NumlockOn, MouseFlags.Button1Pressed),
  406. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NumlockOn, MouseFlags.Button1Released),
  407. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NumlockOn, MouseFlags.None)
  408. }
  409. ];
  410. yield return
  411. [
  412. new []
  413. {
  414. Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.ScrolllockOn, MouseFlags.Button1Pressed),
  415. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.ScrolllockOn, MouseFlags.Button1Released),
  416. Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.ScrolllockOn, MouseFlags.None)
  417. }
  418. ];
  419. }
  420. [Theory]
  421. [MemberData (nameof (MouseFlagTestData))]
  422. internal void MouseFlags_Should_Map_Correctly (Tuple<ButtonState, EventFlags, ControlKeyState, MouseFlags> [] inputOutputPairs)
  423. {
  424. var processor = new WindowsInputProcessor (new ());
  425. foreach (Tuple<ButtonState, EventFlags, ControlKeyState, MouseFlags> pair in inputOutputPairs)
  426. {
  427. var mockEvent = new MouseEventRecord { ButtonState = pair.Item1, EventFlags = pair.Item2, ControlKeyState = pair.Item3 };
  428. MouseEventArgs result = processor.ToMouseEvent (mockEvent);
  429. Assert.Equal (pair.Item4, result.Flags);
  430. }
  431. }
  432. }