using System.Collections.Concurrent; using InputRecord = Terminal.Gui.Drivers.WindowsConsole.InputRecord; using ButtonState = Terminal.Gui.Drivers.WindowsConsole.ButtonState; using EventFlags = Terminal.Gui.Drivers.WindowsConsole.EventFlags; using ControlKeyState = Terminal.Gui.Drivers.WindowsConsole.ControlKeyState; using MouseEventRecord = Terminal.Gui.Drivers.WindowsConsole.MouseEventRecord; namespace DriverTests; public class WindowsInputProcessorTests { [Fact] public void Test_ProcessQueue_CapitalHLowerE () { ConcurrentQueue queue = new (); queue.Enqueue ( new () { EventType = WindowsConsole.EventType.Key, KeyEvent = new () { bKeyDown = true, UnicodeChar = 'H', dwControlKeyState = ControlKeyState.CapslockOn, wVirtualKeyCode = (VK)72, wVirtualScanCode = 35 } }); queue.Enqueue ( new () { EventType = WindowsConsole.EventType.Key, KeyEvent = new () { bKeyDown = false, UnicodeChar = 'H', dwControlKeyState = ControlKeyState.CapslockOn, wVirtualKeyCode = (VK)72, wVirtualScanCode = 35 } }); queue.Enqueue ( new () { EventType = WindowsConsole.EventType.Key, KeyEvent = new () { bKeyDown = true, UnicodeChar = 'i', dwControlKeyState = ControlKeyState.NoControlKeyPressed, wVirtualKeyCode = (VK)73, wVirtualScanCode = 23 } }); queue.Enqueue ( new () { EventType = WindowsConsole.EventType.Key, KeyEvent = new () { bKeyDown = false, UnicodeChar = 'i', dwControlKeyState = ControlKeyState.NoControlKeyPressed, wVirtualKeyCode = (VK)73, wVirtualScanCode = 23 } }); var processor = new WindowsInputProcessor (queue); List ups = []; List downs = []; processor.KeyUp += (s, e) => { ups.Add (e); }; processor.KeyDown += (s, e) => { downs.Add (e); }; Assert.Empty (ups); Assert.Empty (downs); processor.ProcessQueue (); Assert.Equal (Key.H.WithShift, ups [0]); Assert.Equal (Key.H.WithShift, downs [0]); Assert.Equal (Key.I, ups [1]); Assert.Equal (Key.I, downs [1]); } [Fact] public void Test_ProcessQueue_Mouse_Move () { ConcurrentQueue queue = new (); queue.Enqueue ( new () { EventType = WindowsConsole.EventType.Mouse, MouseEvent = new () { MousePosition = new (32, 31), ButtonState = ButtonState.NoButtonPressed, ControlKeyState = ControlKeyState.NoControlKeyPressed, EventFlags = EventFlags.MouseMoved } }); var processor = new WindowsInputProcessor (queue); List mouseEvents = []; processor.MouseEvent += (s, e) => { mouseEvents.Add (e); }; Assert.Empty (mouseEvents); processor.ProcessQueue (); MouseEventArgs s = Assert.Single (mouseEvents); Assert.Equal (MouseFlags.ReportMousePosition, s.Flags); Assert.Equal (s.ScreenPosition, new (32, 31)); } [Theory] [InlineData (ButtonState.Button1Pressed, MouseFlags.Button1Pressed)] [InlineData (ButtonState.Button2Pressed, MouseFlags.Button2Pressed)] [InlineData (ButtonState.Button3Pressed, MouseFlags.Button3Pressed)] [InlineData (ButtonState.Button4Pressed, MouseFlags.Button4Pressed)] internal void Test_ProcessQueue_Mouse_Pressed (ButtonState state, MouseFlags expectedFlag) { ConcurrentQueue queue = new (); queue.Enqueue ( new () { EventType = WindowsConsole.EventType.Mouse, MouseEvent = new () { MousePosition = new (32, 31), ButtonState = state, ControlKeyState = ControlKeyState.NoControlKeyPressed, EventFlags = EventFlags.MouseMoved } }); var processor = new WindowsInputProcessor (queue); List mouseEvents = []; processor.MouseEvent += (s, e) => { mouseEvents.Add (e); }; Assert.Empty (mouseEvents); processor.ProcessQueue (); MouseEventArgs s = Assert.Single (mouseEvents); Assert.Equal (s.Flags, MouseFlags.ReportMousePosition | expectedFlag); Assert.Equal (s.ScreenPosition, new (32, 31)); } [Theory] [InlineData (100, MouseFlags.WheeledUp)] [InlineData (-100, MouseFlags.WheeledDown)] internal void Test_ProcessQueue_Mouse_Wheel (int wheelValue, MouseFlags expectedFlag) { ConcurrentQueue queue = new (); queue.Enqueue ( new () { EventType = WindowsConsole.EventType.Mouse, MouseEvent = new () { MousePosition = new (32, 31), ButtonState = (ButtonState)wheelValue, ControlKeyState = ControlKeyState.NoControlKeyPressed, EventFlags = EventFlags.MouseWheeled } }); var processor = new WindowsInputProcessor (queue); List mouseEvents = []; processor.MouseEvent += (s, e) => { mouseEvents.Add (e); }; Assert.Empty (mouseEvents); processor.ProcessQueue (); MouseEventArgs s = Assert.Single (mouseEvents); Assert.Equal (s.Flags, expectedFlag); Assert.Equal (s.ScreenPosition, new (32, 31)); } public static IEnumerable MouseFlagTestData () { yield return [ new [] { Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button1Pressed), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button1Released), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None) } ]; yield return [ new [] { Tuple.Create ( ButtonState.Button2Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button2Pressed | MouseFlags.ReportMousePosition), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button2Released), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None) } ]; yield return [ new [] { Tuple.Create ( ButtonState.Button3Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Released), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None) } ]; yield return [ new [] { Tuple.Create ( ButtonState.Button4Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button4Pressed | MouseFlags.ReportMousePosition), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button4Released), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.ReportMousePosition) } ]; yield return [ new [] { Tuple.Create ( ButtonState.RightmostButtonPressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Released), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None) } ]; // Tests for holding down 2 buttons at once and releasing them one after the other yield return [ new [] { Tuple.Create ( ButtonState.Button1Pressed | ButtonState.Button2Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button1Pressed | MouseFlags.Button2Pressed | MouseFlags.ReportMousePosition), Tuple.Create ( ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button1Pressed | MouseFlags.Button2Released), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button1Released), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None) } ]; yield return [ new [] { Tuple.Create ( ButtonState.Button3Pressed | ButtonState.Button4Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Pressed | MouseFlags.Button4Pressed | MouseFlags.ReportMousePosition), Tuple.Create ( ButtonState.Button3Pressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Pressed | MouseFlags.Button4Released), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Released), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None) } ]; // Test for holding down 2 buttons at once and releasing them simultaneously yield return [ new [] { Tuple.Create ( ButtonState.Button1Pressed | ButtonState.Button2Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button1Pressed | MouseFlags.Button2Pressed | MouseFlags.ReportMousePosition), Tuple.Create ( ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button1Released | MouseFlags.Button2Released), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None) } ]; // Test that rightmost and button 3 are the same button so 2 states is still only 1 flag yield return [ new [] { Tuple.Create ( ButtonState.Button3Pressed | ButtonState.RightmostButtonPressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition), // Can swap between without raising the released Tuple.Create ( ButtonState.Button3Pressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition), Tuple.Create ( ButtonState.RightmostButtonPressed, EventFlags.MouseMoved, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Pressed | MouseFlags.ReportMousePosition), // Now with neither we get released Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.Button3Released), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NoControlKeyPressed, MouseFlags.None) } ]; // Test for ControlKeyState buttons pressed and handled yield return [ new [] { Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.LeftAltPressed, MouseFlags.Button1Pressed | MouseFlags.ButtonAlt), Tuple.Create ( ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.LeftAltPressed, MouseFlags.Button1Released | MouseFlags.ButtonAlt), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.LeftAltPressed, MouseFlags.None | MouseFlags.ButtonAlt) } ]; yield return [ new [] { Tuple.Create ( ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.RightAltPressed, MouseFlags.Button1Pressed | MouseFlags.ButtonAlt), Tuple.Create ( ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.RightAltPressed, MouseFlags.Button1Released | MouseFlags.ButtonAlt), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.RightAltPressed, MouseFlags.None | MouseFlags.ButtonAlt) } ]; yield return [ new [] { Tuple.Create ( ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.LeftControlPressed, MouseFlags.Button1Pressed | MouseFlags.ButtonCtrl), Tuple.Create ( ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.LeftControlPressed, MouseFlags.Button1Released | MouseFlags.ButtonCtrl), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.LeftControlPressed, MouseFlags.None | MouseFlags.ButtonCtrl) } ]; yield return [ new [] { Tuple.Create ( ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.RightControlPressed, MouseFlags.Button1Pressed | MouseFlags.ButtonCtrl), Tuple.Create ( ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.RightControlPressed, MouseFlags.Button1Released | MouseFlags.ButtonCtrl), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.RightControlPressed, MouseFlags.None | MouseFlags.ButtonCtrl) } ]; yield return [ new [] { Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.ShiftPressed, MouseFlags.Button1Pressed | MouseFlags.ButtonShift), Tuple.Create ( ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.ShiftPressed, MouseFlags.Button1Released | MouseFlags.ButtonShift), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.ShiftPressed, MouseFlags.None | MouseFlags.ButtonShift) } ]; // Test for ControlKeyState buttons pressed and not handled yield return [ new [] { Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.CapslockOn, MouseFlags.Button1Pressed), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.CapslockOn, MouseFlags.Button1Released), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.CapslockOn, MouseFlags.None) } ]; yield return [ new [] { Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.EnhancedKey, MouseFlags.Button1Pressed), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.EnhancedKey, MouseFlags.Button1Released), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.EnhancedKey, MouseFlags.None) } ]; yield return [ new [] { Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.NumlockOn, MouseFlags.Button1Pressed), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NumlockOn, MouseFlags.Button1Released), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.NumlockOn, MouseFlags.None) } ]; yield return [ new [] { Tuple.Create (ButtonState.Button1Pressed, EventFlags.NoEvent, ControlKeyState.ScrolllockOn, MouseFlags.Button1Pressed), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.ScrolllockOn, MouseFlags.Button1Released), Tuple.Create (ButtonState.NoButtonPressed, EventFlags.NoEvent, ControlKeyState.ScrolllockOn, MouseFlags.None) } ]; } [Theory] [MemberData (nameof (MouseFlagTestData))] internal void MouseFlags_Should_Map_Correctly (Tuple [] inputOutputPairs) { var processor = new WindowsInputProcessor (new ()); foreach (Tuple pair in inputOutputPairs) { var mockEvent = new MouseEventRecord { ButtonState = pair.Item1, EventFlags = pair.Item2, ControlKeyState = pair.Item3 }; MouseEventArgs result = processor.ToMouseEvent (mockEvent); Assert.Equal (pair.Item4, result.Flags); } } }