#nullable enable using System.Collections.Concurrent; namespace Terminal.Gui.Drivers; using InputRecord = WindowsConsole.InputRecord; /// /// Input processor for , deals in stream. /// internal class WindowsInputProcessor : InputProcessor { private readonly bool [] _lastWasPressed = new bool[4]; /// public WindowsInputProcessor (ConcurrentQueue inputBuffer) : base (inputBuffer, new WindowsKeyConverter ()) { DriverName = "win"; } /// protected override void Process (InputRecord inputEvent) { switch (inputEvent.EventType) { case WindowsConsole.EventType.Key: // TODO: For now ignore keyup because ANSI comes in as down+up which is confusing to try and parse/pair these things up if (!inputEvent.KeyEvent.bKeyDown) { return; } foreach (Tuple released in Parser.ProcessInput (Tuple.Create (inputEvent.KeyEvent.UnicodeChar, inputEvent))) { ProcessAfterParsing (released.Item2); } /* if (inputEvent.KeyEvent.wVirtualKeyCode == (VK)ConsoleKey.Packet) { // Used to pass Unicode characters as if they were keystrokes. // The VK_PACKET key is the low word of a 32-bit // Virtual Key value used for non-keyboard input methods. inputEvent.KeyEvent = FromVKPacketToKeyEventRecord (inputEvent.KeyEvent); } WindowsConsole.ConsoleKeyInfoEx keyInfo = ToConsoleKeyInfoEx (inputEvent.KeyEvent); //Debug.WriteLine ($"event: KBD: {GetKeyboardLayoutName()} {inputEvent.ToString ()} {keyInfo.ToString (keyInfo)}"); KeyCode map = MapKey (keyInfo); if (map == KeyCode.Null) { break; } */ // This follows convention in NetDriver break; case WindowsConsole.EventType.Mouse: MouseEventArgs me = ToDriverMouse (inputEvent.MouseEvent); OnMouseEvent (me); break; } } /// protected override void ProcessAfterParsing (InputRecord input) { var key = KeyConverter.ToKey (input); // If the key is not valid, we don't want to raise any events. if (IsValidInput (key, out key)) { OnKeyDown (key!); OnKeyUp (key!); } } public MouseEventArgs ToDriverMouse (WindowsConsole.MouseEventRecord e) { var mouseFlags = MouseFlags.None; mouseFlags = UpdateMouseFlags ( mouseFlags, e.ButtonState, WindowsConsole.ButtonState.Button1Pressed, MouseFlags.Button1Pressed, MouseFlags.Button1Released, 0); mouseFlags = UpdateMouseFlags ( mouseFlags, e.ButtonState, WindowsConsole.ButtonState.Button2Pressed, MouseFlags.Button2Pressed, MouseFlags.Button2Released, 1); mouseFlags = UpdateMouseFlags ( mouseFlags, e.ButtonState, WindowsConsole.ButtonState.Button4Pressed, MouseFlags.Button4Pressed, MouseFlags.Button4Released, 3); // Deal with button 3 separately because it is considered same as 'rightmost button' if (e.ButtonState.HasFlag (WindowsConsole.ButtonState.Button3Pressed) || e.ButtonState.HasFlag (WindowsConsole.ButtonState.RightmostButtonPressed)) { mouseFlags |= MouseFlags.Button3Pressed; _lastWasPressed [2] = true; } else { if (_lastWasPressed [2]) { mouseFlags |= MouseFlags.Button3Released; _lastWasPressed [2] = false; } } if (e.EventFlags == WindowsConsole.EventFlags.MouseWheeled) { switch ((int)e.ButtonState) { case > 0: mouseFlags = MouseFlags.WheeledUp; break; case < 0: mouseFlags = MouseFlags.WheeledDown; break; } } if (e.EventFlags != WindowsConsole.EventFlags.NoEvent) { switch (e.EventFlags) { case WindowsConsole.EventFlags.MouseMoved: mouseFlags |= MouseFlags.ReportMousePosition; break; } } if (e.ControlKeyState != WindowsConsole.ControlKeyState.NoControlKeyPressed) { switch (e.ControlKeyState) { case WindowsConsole.ControlKeyState.RightAltPressed: case WindowsConsole.ControlKeyState.LeftAltPressed: mouseFlags |= MouseFlags.ButtonAlt; break; case WindowsConsole.ControlKeyState.RightControlPressed: case WindowsConsole.ControlKeyState.LeftControlPressed: mouseFlags |= MouseFlags.ButtonCtrl; break; case WindowsConsole.ControlKeyState.ShiftPressed: mouseFlags |= MouseFlags.ButtonShift; break; } } var result = new MouseEventArgs { Position = new (e.MousePosition.X, e.MousePosition.Y), Flags = mouseFlags }; return result; } private MouseFlags UpdateMouseFlags ( MouseFlags current, WindowsConsole.ButtonState newState, WindowsConsole.ButtonState pressedState, MouseFlags pressedFlag, MouseFlags releasedFlag, int buttonIndex ) { if (newState.HasFlag (pressedState)) { current |= pressedFlag; _lastWasPressed [buttonIndex] = true; } else { if (_lastWasPressed [buttonIndex]) { current |= releasedFlag; _lastWasPressed [buttonIndex] = false; } } return current; } }