WindowsInputProcessor.cs 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. #nullable enable
  2. using System.Collections.Concurrent;
  3. namespace Terminal.Gui.Drivers;
  4. using InputRecord = WindowsConsole.InputRecord;
  5. /// <summary>
  6. /// Input processor for <see cref="WindowsInput"/>, deals in <see cref="WindowsConsole.InputRecord"/> stream.
  7. /// </summary>
  8. internal class WindowsInputProcessor : InputProcessorImpl<InputRecord>
  9. {
  10. private readonly bool [] _lastWasPressed = new bool [4];
  11. /// <inheritdoc/>
  12. public WindowsInputProcessor (ConcurrentQueue<InputRecord> inputBuffer) : base (inputBuffer, new WindowsKeyConverter ())
  13. {
  14. DriverName = "windows";
  15. }
  16. /// <inheritdoc />
  17. public override void EnqueueMouseEvent (MouseEventArgs mouseEvent)
  18. {
  19. InputQueue.Enqueue (new ()
  20. {
  21. EventType = WindowsConsole.EventType.Mouse,
  22. MouseEvent = ToMouseEventRecord (mouseEvent)
  23. });
  24. }
  25. /// <inheritdoc/>
  26. protected override void Process (InputRecord inputEvent)
  27. {
  28. switch (inputEvent.EventType)
  29. {
  30. case WindowsConsole.EventType.Key:
  31. // TODO: v1 supported distinct key up/down events on Windows.
  32. // TODO: For now ignore keyup because ANSI comes in as down+up which is confusing to try and parse/pair these things up
  33. if (!inputEvent.KeyEvent.bKeyDown)
  34. {
  35. return;
  36. }
  37. foreach (Tuple<char, InputRecord> released in Parser.ProcessInput (Tuple.Create (inputEvent.KeyEvent.UnicodeChar, inputEvent)))
  38. {
  39. ProcessAfterParsing (released.Item2);
  40. }
  41. /*
  42. if (inputEvent.KeyEvent.wVirtualKeyCode == (VK)ConsoleKey.Packet)
  43. {
  44. // Used to pass Unicode characters as if they were keystrokes.
  45. // The VK_PACKET key is the low word of a 32-bit
  46. // Virtual Key value used for non-keyboard input methods.
  47. inputEvent.KeyEvent = FromVKPacketToKeyEventRecord (inputEvent.KeyEvent);
  48. }
  49. WindowsConsole.ConsoleKeyInfoEx keyInfo = ToConsoleKeyInfoEx (inputEvent.KeyEvent);
  50. //Debug.WriteLine ($"event: KBD: {GetKeyboardLayoutName()} {inputEvent.ToString ()} {keyInfo.ToString (keyInfo)}");
  51. KeyCode map = MapKey (keyInfo);
  52. if (map == KeyCode.Null)
  53. {
  54. break;
  55. }
  56. */
  57. // This follows convention in DotNetDriver
  58. break;
  59. case WindowsConsole.EventType.Mouse:
  60. MouseEventArgs me = ToMouseEvent (inputEvent.MouseEvent);
  61. RaiseMouseEvent (me);
  62. break;
  63. }
  64. }
  65. /// <summary>
  66. /// Converts a Windows-specific mouse event to a <see cref="MouseEventArgs"/>.
  67. /// </summary>
  68. /// <param name="e"></param>
  69. /// <returns></returns>
  70. public MouseEventArgs ToMouseEvent (WindowsConsole.MouseEventRecord e)
  71. {
  72. var mouseFlags = MouseFlags.None;
  73. mouseFlags = UpdateMouseFlags (
  74. mouseFlags,
  75. e.ButtonState,
  76. WindowsConsole.ButtonState.Button1Pressed,
  77. MouseFlags.Button1Pressed,
  78. MouseFlags.Button1Released,
  79. 0);
  80. mouseFlags = UpdateMouseFlags (
  81. mouseFlags,
  82. e.ButtonState,
  83. WindowsConsole.ButtonState.Button2Pressed,
  84. MouseFlags.Button2Pressed,
  85. MouseFlags.Button2Released,
  86. 1);
  87. mouseFlags = UpdateMouseFlags (
  88. mouseFlags,
  89. e.ButtonState,
  90. WindowsConsole.ButtonState.Button4Pressed,
  91. MouseFlags.Button4Pressed,
  92. MouseFlags.Button4Released,
  93. 3);
  94. // Deal with button 3 separately because it is considered same as 'rightmost button'
  95. if (e.ButtonState.HasFlag (WindowsConsole.ButtonState.Button3Pressed) || e.ButtonState.HasFlag (WindowsConsole.ButtonState.RightmostButtonPressed))
  96. {
  97. mouseFlags |= MouseFlags.Button3Pressed;
  98. _lastWasPressed [2] = true;
  99. }
  100. else
  101. {
  102. if (_lastWasPressed [2])
  103. {
  104. mouseFlags |= MouseFlags.Button3Released;
  105. _lastWasPressed [2] = false;
  106. }
  107. }
  108. if (e.EventFlags == WindowsConsole.EventFlags.MouseWheeled)
  109. {
  110. switch ((int)e.ButtonState)
  111. {
  112. case > 0:
  113. mouseFlags = MouseFlags.WheeledUp;
  114. break;
  115. case < 0:
  116. mouseFlags = MouseFlags.WheeledDown;
  117. break;
  118. }
  119. }
  120. if (e.EventFlags != WindowsConsole.EventFlags.NoEvent)
  121. {
  122. switch (e.EventFlags)
  123. {
  124. case WindowsConsole.EventFlags.MouseMoved:
  125. mouseFlags |= MouseFlags.ReportMousePosition;
  126. break;
  127. }
  128. }
  129. if (e.ControlKeyState != WindowsConsole.ControlKeyState.NoControlKeyPressed)
  130. {
  131. switch (e.ControlKeyState)
  132. {
  133. case WindowsConsole.ControlKeyState.RightAltPressed:
  134. case WindowsConsole.ControlKeyState.LeftAltPressed:
  135. mouseFlags |= MouseFlags.ButtonAlt;
  136. break;
  137. case WindowsConsole.ControlKeyState.RightControlPressed:
  138. case WindowsConsole.ControlKeyState.LeftControlPressed:
  139. mouseFlags |= MouseFlags.ButtonCtrl;
  140. break;
  141. case WindowsConsole.ControlKeyState.ShiftPressed:
  142. mouseFlags |= MouseFlags.ButtonShift;
  143. break;
  144. }
  145. }
  146. var result = new MouseEventArgs
  147. {
  148. Position = new (e.MousePosition.X, e.MousePosition.Y),
  149. Flags = mouseFlags
  150. };
  151. return result;
  152. }
  153. private MouseFlags UpdateMouseFlags (
  154. MouseFlags current,
  155. WindowsConsole.ButtonState newState,
  156. WindowsConsole.ButtonState pressedState,
  157. MouseFlags pressedFlag,
  158. MouseFlags releasedFlag,
  159. int buttonIndex
  160. )
  161. {
  162. if (newState.HasFlag (pressedState))
  163. {
  164. current |= pressedFlag;
  165. _lastWasPressed [buttonIndex] = true;
  166. }
  167. else
  168. {
  169. if (_lastWasPressed [buttonIndex])
  170. {
  171. current |= releasedFlag;
  172. _lastWasPressed [buttonIndex] = false;
  173. }
  174. }
  175. return current;
  176. }
  177. /// <summary>
  178. /// Converts a <see cref="MouseEventArgs"/> to a Windows-specific <see cref="WindowsConsole.MouseEventRecord"/>.
  179. /// </summary>
  180. /// <param name="mouseEvent"></param>
  181. /// <returns></returns>
  182. public WindowsConsole.MouseEventRecord ToMouseEventRecord (MouseEventArgs mouseEvent)
  183. {
  184. var buttonState = WindowsConsole.ButtonState.NoButtonPressed;
  185. var eventFlags = WindowsConsole.EventFlags.NoEvent;
  186. var controlKeyState = WindowsConsole.ControlKeyState.NoControlKeyPressed;
  187. // Convert button states
  188. if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed))
  189. {
  190. buttonState |= WindowsConsole.ButtonState.Button1Pressed;
  191. }
  192. if (mouseEvent.Flags.HasFlag (MouseFlags.Button2Pressed))
  193. {
  194. buttonState |= WindowsConsole.ButtonState.Button2Pressed;
  195. }
  196. if (mouseEvent.Flags.HasFlag (MouseFlags.Button3Pressed))
  197. {
  198. buttonState |= WindowsConsole.ButtonState.Button3Pressed;
  199. }
  200. if (mouseEvent.Flags.HasFlag (MouseFlags.Button4Pressed))
  201. {
  202. buttonState |= WindowsConsole.ButtonState.Button4Pressed;
  203. }
  204. // Convert mouse wheel events
  205. if (mouseEvent.Flags.HasFlag (MouseFlags.WheeledUp))
  206. {
  207. eventFlags = WindowsConsole.EventFlags.MouseWheeled;
  208. buttonState = (WindowsConsole.ButtonState)0x00780000; // Positive value for wheel up
  209. }
  210. else if (mouseEvent.Flags.HasFlag (MouseFlags.WheeledDown))
  211. {
  212. eventFlags = WindowsConsole.EventFlags.MouseWheeled;
  213. buttonState = (WindowsConsole.ButtonState)unchecked((int)0xFF880000); // Negative value for wheel down
  214. }
  215. // Convert movement flag
  216. if (mouseEvent.Flags.HasFlag (MouseFlags.ReportMousePosition))
  217. {
  218. eventFlags |= WindowsConsole.EventFlags.MouseMoved;
  219. }
  220. // Convert modifier keys
  221. if (mouseEvent.Flags.HasFlag (MouseFlags.ButtonAlt))
  222. {
  223. controlKeyState |= WindowsConsole.ControlKeyState.LeftAltPressed;
  224. }
  225. if (mouseEvent.Flags.HasFlag (MouseFlags.ButtonCtrl))
  226. {
  227. controlKeyState |= WindowsConsole.ControlKeyState.LeftControlPressed;
  228. }
  229. if (mouseEvent.Flags.HasFlag (MouseFlags.ButtonShift))
  230. {
  231. controlKeyState |= WindowsConsole.ControlKeyState.ShiftPressed;
  232. }
  233. return new WindowsConsole.MouseEventRecord
  234. {
  235. MousePosition = new WindowsConsole.Coord ((short)mouseEvent.Position.X, (short)mouseEvent.Position.Y),
  236. ButtonState = buttonState,
  237. ControlKeyState = controlKeyState,
  238. EventFlags = eventFlags
  239. };
  240. }
  241. }