records.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. package coninput
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. "strconv"
  6. "strings"
  7. )
  8. const (
  9. maxEventSize = 16
  10. wordPaddingBytes = 2
  11. )
  12. // EventType denots the type of an event
  13. type EventType uint16
  14. // EventUnion is the union data type that contains the data for any event.
  15. type EventUnion [maxEventSize]byte
  16. // InputRecord corresponds to the INPUT_RECORD structure from the Windows
  17. // console API (see
  18. // https://docs.microsoft.com/en-us/windows/console/input-record-str).
  19. type InputRecord struct {
  20. // EventType specifies the type of event that helt in Event.
  21. EventType EventType
  22. // Padding of the 16-bit EventType to a whole 32-bit dword.
  23. _ [wordPaddingBytes]byte
  24. // Event holds the actual event data. Use Unrap to access it as its
  25. // respective event type.
  26. Event EventUnion
  27. }
  28. // String implements fmt.Stringer for InputRecord.
  29. func (ir InputRecord) String() string {
  30. return ir.Unwrap().String()
  31. }
  32. // Unwrap parses the event data into an EventRecord of the respective event
  33. // type. The data in the returned EventRecord does not contain any references to
  34. // the passed InputRecord.
  35. func (ir InputRecord) Unwrap() EventRecord {
  36. switch ir.EventType {
  37. case FocusEventType:
  38. return FocusEventRecord{SetFocus: ir.Event[0] > 0}
  39. case KeyEventType:
  40. return KeyEventRecord{
  41. KeyDown: binary.LittleEndian.Uint32(ir.Event[0:4]) > 0,
  42. RepeatCount: binary.LittleEndian.Uint16(ir.Event[4:6]),
  43. VirtualKeyCode: VirtualKeyCode(binary.LittleEndian.Uint16(ir.Event[6:8])),
  44. VirtualScanCode: VirtualKeyCode(binary.LittleEndian.Uint16(ir.Event[8:10])),
  45. Char: rune(binary.LittleEndian.Uint16(ir.Event[10:12])),
  46. ControlKeyState: ControlKeyState(binary.LittleEndian.Uint32(ir.Event[12:16])),
  47. }
  48. case MouseEventType:
  49. m := MouseEventRecord{
  50. MousePositon: Coord{
  51. X: binary.LittleEndian.Uint16(ir.Event[0:2]),
  52. Y: binary.LittleEndian.Uint16(ir.Event[2:4]),
  53. },
  54. ButtonState: ButtonState(binary.LittleEndian.Uint32(ir.Event[4:8])),
  55. ControlKeyState: ControlKeyState(binary.LittleEndian.Uint32(ir.Event[8:12])),
  56. EventFlags: EventFlags(binary.LittleEndian.Uint32(ir.Event[12:16])),
  57. }
  58. if (m.EventFlags&MOUSE_WHEELED > 0) || (m.EventFlags&MOUSE_HWHEELED > 0) {
  59. if int16(highWord(uint32(m.ButtonState))) > 0 {
  60. m.WheelDirection = 1
  61. } else {
  62. m.WheelDirection = -1
  63. }
  64. }
  65. return m
  66. case WindowBufferSizeEventType:
  67. return WindowBufferSizeEventRecord{
  68. Size: Coord{
  69. X: binary.LittleEndian.Uint16(ir.Event[0:2]),
  70. Y: binary.LittleEndian.Uint16(ir.Event[2:4]),
  71. },
  72. }
  73. case MenuEventType:
  74. return MenuEventRecord{
  75. CommandID: binary.LittleEndian.Uint32(ir.Event[0:4]),
  76. }
  77. default:
  78. return &UnknownEvent{InputRecord: ir}
  79. }
  80. }
  81. // EventRecord represents one of the following event types:
  82. // TypeFocusEventRecord, TypeKeyEventRecord, TypeMouseEventRecord,
  83. // TypeWindowBufferSizeEvent, TypeMenuEventRecord and UnknownEvent.
  84. type EventRecord interface {
  85. Type() string
  86. fmt.Stringer
  87. }
  88. // FocusEventType is the event type for a FocusEventRecord (see
  89. // https://docs.microsoft.com/en-us/windows/console/input-record-str).
  90. const FocusEventType EventType = 0x0010
  91. // FocusEventRecord represent the FOCUS_EVENT_RECORD structure from the Windows
  92. // console API (see
  93. // https://docs.microsoft.com/en-us/windows/console/focus-event-record-str).
  94. // These events are used internally by the Windows console API and should be
  95. // ignored.
  96. type FocusEventRecord struct {
  97. // SetFocus is reserved and should not be used.
  98. SetFocus bool
  99. }
  100. // Ensure that FocusEventRecord satisfies EventRecord interface.
  101. var _ EventRecord = FocusEventRecord{}
  102. // Type ensures that FocusEventRecord satisfies EventRecord interface.
  103. func (e FocusEventRecord) Type() string { return "FocusEvent" }
  104. // String ensures that FocusEventRecord satisfies EventRecord and fmt.Stringer
  105. // interfaces.
  106. func (e FocusEventRecord) String() string { return fmt.Sprintf("%s[%v]", e.Type(), e.SetFocus) }
  107. // KeyEventType is the event type for a KeyEventRecord (see
  108. // https://docs.microsoft.com/en-us/windows/console/input-record-str).
  109. const KeyEventType EventType = 0x0001
  110. // KeyEventRecord represent the KEY_EVENT_RECORD structure from the Windows
  111. // console API (see
  112. // https://docs.microsoft.com/en-us/windows/console/key-event-record-str).
  113. type KeyEventRecord struct {
  114. // KeyDown specified whether the key is pressed or released.
  115. KeyDown bool
  116. // RepeatCount indicates that a key is being held down. For example, when a
  117. // key is held down, five events with RepeatCount equal to 1 may be
  118. // generated, one event with RepeatCount equal to 5, or multiple events
  119. // with RepeatCount greater than or equal to 1.
  120. RepeatCount uint16
  121. // VirtualKeyCode identifies the given key in a device-independent manner
  122. // (see
  123. // https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes).
  124. VirtualKeyCode VirtualKeyCode
  125. // VirtualScanCode represents the device-dependent value generated by the
  126. // keyboard hardware.
  127. VirtualScanCode VirtualKeyCode
  128. // Char is the character that corresponds to the pressed key. Char can be
  129. // zero for some keys.
  130. Char rune
  131. //ControlKeyState holds the state of the control keys.
  132. ControlKeyState ControlKeyState
  133. }
  134. // Ensure that KeyEventRecord satisfies EventRecord interface.
  135. var _ EventRecord = KeyEventRecord{}
  136. // Type ensures that KeyEventRecord satisfies EventRecord interface.
  137. func (e KeyEventRecord) Type() string { return "KeyEvent" }
  138. // String ensures that KeyEventRecord satisfies EventRecord and fmt.Stringer
  139. // interfaces.
  140. func (e KeyEventRecord) String() string {
  141. infos := []string{}
  142. repeat := ""
  143. if e.RepeatCount > 1 {
  144. repeat = "x" + strconv.Itoa(int(e.RepeatCount))
  145. }
  146. infos = append(infos, fmt.Sprintf("%q%s", e.Char, repeat))
  147. direction := "up"
  148. if e.KeyDown {
  149. direction = "down"
  150. }
  151. infos = append(infos, direction)
  152. if e.ControlKeyState != NO_CONTROL_KEY {
  153. infos = append(infos, e.ControlKeyState.String())
  154. }
  155. infos = append(infos, fmt.Sprintf("KeyCode: %d", e.VirtualKeyCode))
  156. infos = append(infos, fmt.Sprintf("ScanCode: %d", e.VirtualScanCode))
  157. return fmt.Sprintf("%s[%s]", e.Type(), strings.Join(infos, ", "))
  158. }
  159. // MenuEventType is the event type for a MenuEventRecord (see
  160. // https://docs.microsoft.com/en-us/windows/console/input-record-str).
  161. const MenuEventType EventType = 0x0008
  162. // MenuEventRecord represent the MENU_EVENT_RECORD structure from the Windows
  163. // console API (see
  164. // https://docs.microsoft.com/en-us/windows/console/menu-event-record-str).
  165. // These events are deprecated by the Windows console API and should be ignored.
  166. type MenuEventRecord struct {
  167. CommandID uint32
  168. }
  169. // Ensure that MenuEventRecord satisfies EventRecord interface.
  170. var _ EventRecord = MenuEventRecord{}
  171. // Type ensures that MenuEventRecord satisfies EventRecord interface.
  172. func (e MenuEventRecord) Type() string { return "MenuEvent" }
  173. // String ensures that MenuEventRecord satisfies EventRecord and fmt.Stringer
  174. // interfaces.
  175. func (e MenuEventRecord) String() string { return fmt.Sprintf("MenuEvent[%d]", e.CommandID) }
  176. // MouseEventType is the event type for a MouseEventRecord (see
  177. // https://docs.microsoft.com/en-us/windows/console/input-record-str).
  178. const MouseEventType EventType = 0x0002
  179. // MouseEventRecord represent the MOUSE_EVENT_RECORD structure from the Windows
  180. // console API (see
  181. // https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str).
  182. type MouseEventRecord struct {
  183. // MousePosition contains the location of the cursor, in terms of the
  184. // console screen buffer's character-cell coordinates.
  185. MousePositon Coord
  186. // ButtonState holds the status of the mouse buttons.
  187. ButtonState ButtonState
  188. // ControlKeyState holds the state of the control keys.
  189. ControlKeyState ControlKeyState
  190. // EventFlags specify tge type of mouse event.
  191. EventFlags EventFlags
  192. // WheelDirection specified the direction in which the mouse wheel is
  193. // spinning when EventFlags contains MOUSE_HWHEELED or MOUSE_WHEELED. When
  194. // the event flags specify MOUSE_WHEELED it is 1 if the wheel rotated
  195. // forward (away from the user) or -1 when it rotates backwards. When
  196. // MOUSE_HWHEELED is specified it is 1 when the wheel rotates right and -1
  197. // when it rotates left. When the EventFlags do not indicate a mouse wheel
  198. // event it is 0.
  199. WheelDirection int
  200. }
  201. // Ensure that MouseEventRecord satisfies EventRecord interface.
  202. var _ EventRecord = MouseEventRecord{}
  203. func (e MouseEventRecord) WheelDirectionName() string {
  204. if e.EventFlags&MOUSE_WHEELED > 0 {
  205. if e.WheelDirection > 0 {
  206. return "Forward"
  207. }
  208. return "Backward"
  209. } else if e.EventFlags&MOUSE_HWHEELED > 0 {
  210. if e.WheelDirection > 0 {
  211. return "Right"
  212. }
  213. return "Left"
  214. }
  215. return ""
  216. }
  217. // Type ensures that MouseEventRecord satisfies EventRecord interface.
  218. func (e MouseEventRecord) Type() string { return "MouseEvent" }
  219. // String ensures that MouseEventRecord satisfies EventRecord and fmt.Stringer
  220. // interfaces.
  221. func (e MouseEventRecord) String() string {
  222. infos := []string{e.MousePositon.String()}
  223. if e.ButtonState&0xFF != 0 {
  224. infos = append(infos, e.ButtonState.String())
  225. }
  226. eventDescription := e.EventFlags.String()
  227. wheelDirection := e.WheelDirectionName()
  228. if wheelDirection != "" {
  229. eventDescription += "(" + wheelDirection + ")"
  230. }
  231. infos = append(infos, eventDescription)
  232. if e.ControlKeyState != NO_CONTROL_KEY {
  233. infos = append(infos, e.ControlKeyState.String())
  234. }
  235. return fmt.Sprintf("%s[%s]", e.Type(), strings.Join(infos, ", "))
  236. }
  237. // WindowBufferSizeEventType is the event type for a WindowBufferSizeEventRecord
  238. // (see https://docs.microsoft.com/en-us/windows/console/input-record-str).
  239. const WindowBufferSizeEventType EventType = 0x0004
  240. // WindowBufferSizeEventRecord represent the WINDOW_BUFFER_SIZE_RECORD structure
  241. // from the Windows console API (see
  242. // https://docs.microsoft.com/en-us/windows/console/window-buffer-size-record-str).
  243. type WindowBufferSizeEventRecord struct {
  244. // Size contains the size of the console screen buffer, in character cell columns and rows.
  245. Size Coord
  246. }
  247. // Ensure that WindowBufferSizeEventRecord satisfies EventRecord interface.
  248. var _ EventRecord = WindowBufferSizeEventRecord{}
  249. // Type ensures that WindowBufferSizeEventRecord satisfies EventRecord interface.
  250. func (e WindowBufferSizeEventRecord) Type() string { return "WindowBufferSizeEvent" }
  251. // String ensures that WindowBufferSizeEventRecord satisfies EventRecord and fmt.Stringer
  252. // interfaces.
  253. func (e WindowBufferSizeEventRecord) String() string {
  254. return fmt.Sprintf("WindowBufferSizeEvent[%s]", e.Size)
  255. }
  256. // UnknownEvent is generated when the event type does not match one of the
  257. // following types: TypeFocusEventRecord, TypeKeyEventRecord,
  258. // TypeMouseEventRecord, TypeWindowBufferSizeEvent, TypeMenuEventRecord and
  259. // UnknownEvent.
  260. type UnknownEvent struct {
  261. InputRecord
  262. }
  263. // Ensure that UnknownEvent satisfies EventRecord interface.
  264. var _ EventRecord = UnknownEvent{}
  265. // Type ensures that UnknownEvent satisfies EventRecord interface.
  266. func (e UnknownEvent) Type() string { return "UnknownEvent" }
  267. // String ensures that UnknownEvent satisfies EventRecord and fmt.Stringer
  268. // interfaces.
  269. func (e UnknownEvent) String() string {
  270. return fmt.Sprintf("%s[Type: %d, Data: %v]", e.Type(), e.InputRecord.EventType, e.InputRecord.Event[:])
  271. }
  272. // Coord represent the COORD structure from the Windows
  273. // console API (see https://docs.microsoft.com/en-us/windows/console/coord-str).
  274. type Coord struct {
  275. // X is the horizontal coordinate or column value. The units depend on the function call.
  276. X uint16
  277. // Y is the vertical coordinate or row value. The units depend on the function call.
  278. Y uint16
  279. }
  280. // String ensures that Coord satisfies the fmt.Stringer interface.
  281. func (c Coord) String() string {
  282. return fmt.Sprintf("(%d, %d)", c.X, c.Y)
  283. }
  284. // ButtonState holds the state of the mouse buttons (see
  285. // https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str).
  286. type ButtonState uint32
  287. func (bs ButtonState) Contains(state ButtonState) bool {
  288. return bs&state > 0
  289. }
  290. // String ensures that ButtonState satisfies the fmt.Stringer interface.
  291. func (bs ButtonState) String() string {
  292. switch {
  293. case bs&FROM_LEFT_1ST_BUTTON_PRESSED > 0:
  294. return "Left"
  295. case bs&FROM_LEFT_2ND_BUTTON_PRESSED > 0:
  296. return "2"
  297. case bs&FROM_LEFT_3RD_BUTTON_PRESSED > 0:
  298. return "3"
  299. case bs&FROM_LEFT_4TH_BUTTON_PRESSED > 0:
  300. return "4"
  301. case bs&RIGHTMOST_BUTTON_PRESSED > 0:
  302. return "Right"
  303. case bs&0xFF == 0:
  304. return "No Button"
  305. default:
  306. return fmt.Sprintf("Unknown(%d)", bs)
  307. }
  308. }
  309. func (bs ButtonState) IsReleased() bool {
  310. return bs&0xff > 0
  311. }
  312. // Valid values for ButtonState.
  313. const (
  314. FROM_LEFT_1ST_BUTTON_PRESSED ButtonState = 0x0001
  315. RIGHTMOST_BUTTON_PRESSED ButtonState = 0x0002
  316. FROM_LEFT_2ND_BUTTON_PRESSED ButtonState = 0x0004
  317. FROM_LEFT_3RD_BUTTON_PRESSED ButtonState = 0x0008
  318. FROM_LEFT_4TH_BUTTON_PRESSED ButtonState = 0x0010
  319. )
  320. // ControlKeyState holds the state of the control keys for key and mouse events
  321. // (see https://docs.microsoft.com/en-us/windows/console/key-event-record-str
  322. // and https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str).
  323. type ControlKeyState uint32
  324. func (cks ControlKeyState) Contains(state ControlKeyState) bool {
  325. return cks&state > 0
  326. }
  327. // Valid values for ControlKeyState.
  328. const (
  329. CAPSLOCK_ON ControlKeyState = 0x0080
  330. ENHANCED_KEY ControlKeyState = 0x0100
  331. LEFT_ALT_PRESSED ControlKeyState = 0x0002
  332. LEFT_CTRL_PRESSED ControlKeyState = 0x0008
  333. NUMLOCK_ON ControlKeyState = 0x0020
  334. RIGHT_ALT_PRESSED ControlKeyState = 0x0001
  335. RIGHT_CTRL_PRESSED ControlKeyState = 0x0004
  336. SCROLLLOCK_ON ControlKeyState = 0x0040
  337. SHIFT_PRESSED ControlKeyState = 0x0010
  338. NO_CONTROL_KEY ControlKeyState = 0x0000
  339. )
  340. // String ensures that ControlKeyState satisfies the fmt.Stringer interface.
  341. func (cks ControlKeyState) String() string {
  342. controlKeys := []string{}
  343. switch {
  344. case cks&CAPSLOCK_ON > 0:
  345. controlKeys = append(controlKeys, "CapsLock")
  346. case cks&ENHANCED_KEY > 0:
  347. controlKeys = append(controlKeys, "Enhanced")
  348. case cks&LEFT_ALT_PRESSED > 0:
  349. controlKeys = append(controlKeys, "Alt")
  350. case cks&LEFT_CTRL_PRESSED > 0:
  351. controlKeys = append(controlKeys, "CTRL")
  352. case cks&NUMLOCK_ON > 0:
  353. controlKeys = append(controlKeys, "NumLock")
  354. case cks&RIGHT_ALT_PRESSED > 0:
  355. controlKeys = append(controlKeys, "RightAlt")
  356. case cks&RIGHT_CTRL_PRESSED > 0:
  357. controlKeys = append(controlKeys, "RightCTRL")
  358. case cks&SCROLLLOCK_ON > 0:
  359. controlKeys = append(controlKeys, "ScrollLock")
  360. case cks&SHIFT_PRESSED > 0:
  361. controlKeys = append(controlKeys, "Shift")
  362. case cks == NO_CONTROL_KEY:
  363. default:
  364. return fmt.Sprintf("Unknown(%d)", cks)
  365. }
  366. return strings.Join(controlKeys, ",")
  367. }
  368. // EventFlags specifies the type of a mouse event (see
  369. // https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str).
  370. type EventFlags uint32
  371. // String ensures that EventFlags satisfies the fmt.Stringer interface.
  372. func (ef EventFlags) String() string {
  373. switch {
  374. case ef&DOUBLE_CLICK > 0:
  375. return "DoubleClick"
  376. case ef&MOUSE_WHEELED > 0:
  377. return "Wheeled"
  378. case ef&MOUSE_MOVED > 0:
  379. return "Moved"
  380. case ef&MOUSE_HWHEELED > 0:
  381. return "HWheeld"
  382. case ef == CLICK:
  383. return "Click"
  384. default:
  385. return fmt.Sprintf("Unknown(%d)", ef)
  386. }
  387. }
  388. func (ef EventFlags) Contains(flag EventFlags) bool {
  389. return ef&flag > 0
  390. }
  391. // Valid values for EventFlags.
  392. const (
  393. CLICK EventFlags = 0x0000
  394. MOUSE_MOVED EventFlags = 0x0001
  395. DOUBLE_CLICK EventFlags = 0x0002
  396. MOUSE_WHEELED EventFlags = 0x0004
  397. MOUSE_HWHEELED EventFlags = 0x0008
  398. )
  399. func highWord(data uint32) uint16 {
  400. return uint16((data & 0xFFFF0000) >> 16)
  401. }