| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486 |
- package coninput
- import (
- "encoding/binary"
- "fmt"
- "strconv"
- "strings"
- )
- const (
- maxEventSize = 16
- wordPaddingBytes = 2
- )
- // EventType denots the type of an event
- type EventType uint16
- // EventUnion is the union data type that contains the data for any event.
- type EventUnion [maxEventSize]byte
- // InputRecord corresponds to the INPUT_RECORD structure from the Windows
- // console API (see
- // https://docs.microsoft.com/en-us/windows/console/input-record-str).
- type InputRecord struct {
- // EventType specifies the type of event that helt in Event.
- EventType EventType
- // Padding of the 16-bit EventType to a whole 32-bit dword.
- _ [wordPaddingBytes]byte
- // Event holds the actual event data. Use Unrap to access it as its
- // respective event type.
- Event EventUnion
- }
- // String implements fmt.Stringer for InputRecord.
- func (ir InputRecord) String() string {
- return ir.Unwrap().String()
- }
- // Unwrap parses the event data into an EventRecord of the respective event
- // type. The data in the returned EventRecord does not contain any references to
- // the passed InputRecord.
- func (ir InputRecord) Unwrap() EventRecord {
- switch ir.EventType {
- case FocusEventType:
- return FocusEventRecord{SetFocus: ir.Event[0] > 0}
- case KeyEventType:
- return KeyEventRecord{
- KeyDown: binary.LittleEndian.Uint32(ir.Event[0:4]) > 0,
- RepeatCount: binary.LittleEndian.Uint16(ir.Event[4:6]),
- VirtualKeyCode: VirtualKeyCode(binary.LittleEndian.Uint16(ir.Event[6:8])),
- VirtualScanCode: VirtualKeyCode(binary.LittleEndian.Uint16(ir.Event[8:10])),
- Char: rune(binary.LittleEndian.Uint16(ir.Event[10:12])),
- ControlKeyState: ControlKeyState(binary.LittleEndian.Uint32(ir.Event[12:16])),
- }
- case MouseEventType:
- m := MouseEventRecord{
- MousePositon: Coord{
- X: binary.LittleEndian.Uint16(ir.Event[0:2]),
- Y: binary.LittleEndian.Uint16(ir.Event[2:4]),
- },
- ButtonState: ButtonState(binary.LittleEndian.Uint32(ir.Event[4:8])),
- ControlKeyState: ControlKeyState(binary.LittleEndian.Uint32(ir.Event[8:12])),
- EventFlags: EventFlags(binary.LittleEndian.Uint32(ir.Event[12:16])),
- }
- if (m.EventFlags&MOUSE_WHEELED > 0) || (m.EventFlags&MOUSE_HWHEELED > 0) {
- if int16(highWord(uint32(m.ButtonState))) > 0 {
- m.WheelDirection = 1
- } else {
- m.WheelDirection = -1
- }
- }
- return m
- case WindowBufferSizeEventType:
- return WindowBufferSizeEventRecord{
- Size: Coord{
- X: binary.LittleEndian.Uint16(ir.Event[0:2]),
- Y: binary.LittleEndian.Uint16(ir.Event[2:4]),
- },
- }
- case MenuEventType:
- return MenuEventRecord{
- CommandID: binary.LittleEndian.Uint32(ir.Event[0:4]),
- }
- default:
- return &UnknownEvent{InputRecord: ir}
- }
- }
- // EventRecord represents one of the following event types:
- // TypeFocusEventRecord, TypeKeyEventRecord, TypeMouseEventRecord,
- // TypeWindowBufferSizeEvent, TypeMenuEventRecord and UnknownEvent.
- type EventRecord interface {
- Type() string
- fmt.Stringer
- }
- // FocusEventType is the event type for a FocusEventRecord (see
- // https://docs.microsoft.com/en-us/windows/console/input-record-str).
- const FocusEventType EventType = 0x0010
- // FocusEventRecord represent the FOCUS_EVENT_RECORD structure from the Windows
- // console API (see
- // https://docs.microsoft.com/en-us/windows/console/focus-event-record-str).
- // These events are used internally by the Windows console API and should be
- // ignored.
- type FocusEventRecord struct {
- // SetFocus is reserved and should not be used.
- SetFocus bool
- }
- // Ensure that FocusEventRecord satisfies EventRecord interface.
- var _ EventRecord = FocusEventRecord{}
- // Type ensures that FocusEventRecord satisfies EventRecord interface.
- func (e FocusEventRecord) Type() string { return "FocusEvent" }
- // String ensures that FocusEventRecord satisfies EventRecord and fmt.Stringer
- // interfaces.
- func (e FocusEventRecord) String() string { return fmt.Sprintf("%s[%v]", e.Type(), e.SetFocus) }
- // KeyEventType is the event type for a KeyEventRecord (see
- // https://docs.microsoft.com/en-us/windows/console/input-record-str).
- const KeyEventType EventType = 0x0001
- // KeyEventRecord represent the KEY_EVENT_RECORD structure from the Windows
- // console API (see
- // https://docs.microsoft.com/en-us/windows/console/key-event-record-str).
- type KeyEventRecord struct {
- // KeyDown specified whether the key is pressed or released.
- KeyDown bool
- // RepeatCount indicates that a key is being held down. For example, when a
- // key is held down, five events with RepeatCount equal to 1 may be
- // generated, one event with RepeatCount equal to 5, or multiple events
- // with RepeatCount greater than or equal to 1.
- RepeatCount uint16
- // VirtualKeyCode identifies the given key in a device-independent manner
- // (see
- // https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes).
- VirtualKeyCode VirtualKeyCode
- // VirtualScanCode represents the device-dependent value generated by the
- // keyboard hardware.
- VirtualScanCode VirtualKeyCode
- // Char is the character that corresponds to the pressed key. Char can be
- // zero for some keys.
- Char rune
- //ControlKeyState holds the state of the control keys.
- ControlKeyState ControlKeyState
- }
- // Ensure that KeyEventRecord satisfies EventRecord interface.
- var _ EventRecord = KeyEventRecord{}
- // Type ensures that KeyEventRecord satisfies EventRecord interface.
- func (e KeyEventRecord) Type() string { return "KeyEvent" }
- // String ensures that KeyEventRecord satisfies EventRecord and fmt.Stringer
- // interfaces.
- func (e KeyEventRecord) String() string {
- infos := []string{}
- repeat := ""
- if e.RepeatCount > 1 {
- repeat = "x" + strconv.Itoa(int(e.RepeatCount))
- }
- infos = append(infos, fmt.Sprintf("%q%s", e.Char, repeat))
- direction := "up"
- if e.KeyDown {
- direction = "down"
- }
- infos = append(infos, direction)
- if e.ControlKeyState != NO_CONTROL_KEY {
- infos = append(infos, e.ControlKeyState.String())
- }
- infos = append(infos, fmt.Sprintf("KeyCode: %d", e.VirtualKeyCode))
- infos = append(infos, fmt.Sprintf("ScanCode: %d", e.VirtualScanCode))
- return fmt.Sprintf("%s[%s]", e.Type(), strings.Join(infos, ", "))
- }
- // MenuEventType is the event type for a MenuEventRecord (see
- // https://docs.microsoft.com/en-us/windows/console/input-record-str).
- const MenuEventType EventType = 0x0008
- // MenuEventRecord represent the MENU_EVENT_RECORD structure from the Windows
- // console API (see
- // https://docs.microsoft.com/en-us/windows/console/menu-event-record-str).
- // These events are deprecated by the Windows console API and should be ignored.
- type MenuEventRecord struct {
- CommandID uint32
- }
- // Ensure that MenuEventRecord satisfies EventRecord interface.
- var _ EventRecord = MenuEventRecord{}
- // Type ensures that MenuEventRecord satisfies EventRecord interface.
- func (e MenuEventRecord) Type() string { return "MenuEvent" }
- // String ensures that MenuEventRecord satisfies EventRecord and fmt.Stringer
- // interfaces.
- func (e MenuEventRecord) String() string { return fmt.Sprintf("MenuEvent[%d]", e.CommandID) }
- // MouseEventType is the event type for a MouseEventRecord (see
- // https://docs.microsoft.com/en-us/windows/console/input-record-str).
- const MouseEventType EventType = 0x0002
- // MouseEventRecord represent the MOUSE_EVENT_RECORD structure from the Windows
- // console API (see
- // https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str).
- type MouseEventRecord struct {
- // MousePosition contains the location of the cursor, in terms of the
- // console screen buffer's character-cell coordinates.
- MousePositon Coord
- // ButtonState holds the status of the mouse buttons.
- ButtonState ButtonState
- // ControlKeyState holds the state of the control keys.
- ControlKeyState ControlKeyState
- // EventFlags specify tge type of mouse event.
- EventFlags EventFlags
- // WheelDirection specified the direction in which the mouse wheel is
- // spinning when EventFlags contains MOUSE_HWHEELED or MOUSE_WHEELED. When
- // the event flags specify MOUSE_WHEELED it is 1 if the wheel rotated
- // forward (away from the user) or -1 when it rotates backwards. When
- // MOUSE_HWHEELED is specified it is 1 when the wheel rotates right and -1
- // when it rotates left. When the EventFlags do not indicate a mouse wheel
- // event it is 0.
- WheelDirection int
- }
- // Ensure that MouseEventRecord satisfies EventRecord interface.
- var _ EventRecord = MouseEventRecord{}
- func (e MouseEventRecord) WheelDirectionName() string {
- if e.EventFlags&MOUSE_WHEELED > 0 {
- if e.WheelDirection > 0 {
- return "Forward"
- }
- return "Backward"
- } else if e.EventFlags&MOUSE_HWHEELED > 0 {
- if e.WheelDirection > 0 {
- return "Right"
- }
- return "Left"
- }
- return ""
- }
- // Type ensures that MouseEventRecord satisfies EventRecord interface.
- func (e MouseEventRecord) Type() string { return "MouseEvent" }
- // String ensures that MouseEventRecord satisfies EventRecord and fmt.Stringer
- // interfaces.
- func (e MouseEventRecord) String() string {
- infos := []string{e.MousePositon.String()}
- if e.ButtonState&0xFF != 0 {
- infos = append(infos, e.ButtonState.String())
- }
- eventDescription := e.EventFlags.String()
- wheelDirection := e.WheelDirectionName()
- if wheelDirection != "" {
- eventDescription += "(" + wheelDirection + ")"
- }
- infos = append(infos, eventDescription)
- if e.ControlKeyState != NO_CONTROL_KEY {
- infos = append(infos, e.ControlKeyState.String())
- }
- return fmt.Sprintf("%s[%s]", e.Type(), strings.Join(infos, ", "))
- }
- // WindowBufferSizeEventType is the event type for a WindowBufferSizeEventRecord
- // (see https://docs.microsoft.com/en-us/windows/console/input-record-str).
- const WindowBufferSizeEventType EventType = 0x0004
- // WindowBufferSizeEventRecord represent the WINDOW_BUFFER_SIZE_RECORD structure
- // from the Windows console API (see
- // https://docs.microsoft.com/en-us/windows/console/window-buffer-size-record-str).
- type WindowBufferSizeEventRecord struct {
- // Size contains the size of the console screen buffer, in character cell columns and rows.
- Size Coord
- }
- // Ensure that WindowBufferSizeEventRecord satisfies EventRecord interface.
- var _ EventRecord = WindowBufferSizeEventRecord{}
- // Type ensures that WindowBufferSizeEventRecord satisfies EventRecord interface.
- func (e WindowBufferSizeEventRecord) Type() string { return "WindowBufferSizeEvent" }
- // String ensures that WindowBufferSizeEventRecord satisfies EventRecord and fmt.Stringer
- // interfaces.
- func (e WindowBufferSizeEventRecord) String() string {
- return fmt.Sprintf("WindowBufferSizeEvent[%s]", e.Size)
- }
- // UnknownEvent is generated when the event type does not match one of the
- // following types: TypeFocusEventRecord, TypeKeyEventRecord,
- // TypeMouseEventRecord, TypeWindowBufferSizeEvent, TypeMenuEventRecord and
- // UnknownEvent.
- type UnknownEvent struct {
- InputRecord
- }
- // Ensure that UnknownEvent satisfies EventRecord interface.
- var _ EventRecord = UnknownEvent{}
- // Type ensures that UnknownEvent satisfies EventRecord interface.
- func (e UnknownEvent) Type() string { return "UnknownEvent" }
- // String ensures that UnknownEvent satisfies EventRecord and fmt.Stringer
- // interfaces.
- func (e UnknownEvent) String() string {
- return fmt.Sprintf("%s[Type: %d, Data: %v]", e.Type(), e.InputRecord.EventType, e.InputRecord.Event[:])
- }
- // Coord represent the COORD structure from the Windows
- // console API (see https://docs.microsoft.com/en-us/windows/console/coord-str).
- type Coord struct {
- // X is the horizontal coordinate or column value. The units depend on the function call.
- X uint16
- // Y is the vertical coordinate or row value. The units depend on the function call.
- Y uint16
- }
- // String ensures that Coord satisfies the fmt.Stringer interface.
- func (c Coord) String() string {
- return fmt.Sprintf("(%d, %d)", c.X, c.Y)
- }
- // ButtonState holds the state of the mouse buttons (see
- // https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str).
- type ButtonState uint32
- func (bs ButtonState) Contains(state ButtonState) bool {
- return bs&state > 0
- }
- // String ensures that ButtonState satisfies the fmt.Stringer interface.
- func (bs ButtonState) String() string {
- switch {
- case bs&FROM_LEFT_1ST_BUTTON_PRESSED > 0:
- return "Left"
- case bs&FROM_LEFT_2ND_BUTTON_PRESSED > 0:
- return "2"
- case bs&FROM_LEFT_3RD_BUTTON_PRESSED > 0:
- return "3"
- case bs&FROM_LEFT_4TH_BUTTON_PRESSED > 0:
- return "4"
- case bs&RIGHTMOST_BUTTON_PRESSED > 0:
- return "Right"
- case bs&0xFF == 0:
- return "No Button"
- default:
- return fmt.Sprintf("Unknown(%d)", bs)
- }
- }
- func (bs ButtonState) IsReleased() bool {
- return bs&0xff > 0
- }
- // Valid values for ButtonState.
- const (
- FROM_LEFT_1ST_BUTTON_PRESSED ButtonState = 0x0001
- RIGHTMOST_BUTTON_PRESSED ButtonState = 0x0002
- FROM_LEFT_2ND_BUTTON_PRESSED ButtonState = 0x0004
- FROM_LEFT_3RD_BUTTON_PRESSED ButtonState = 0x0008
- FROM_LEFT_4TH_BUTTON_PRESSED ButtonState = 0x0010
- )
- // ControlKeyState holds the state of the control keys for key and mouse events
- // (see https://docs.microsoft.com/en-us/windows/console/key-event-record-str
- // and https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str).
- type ControlKeyState uint32
- func (cks ControlKeyState) Contains(state ControlKeyState) bool {
- return cks&state > 0
- }
- // Valid values for ControlKeyState.
- const (
- CAPSLOCK_ON ControlKeyState = 0x0080
- ENHANCED_KEY ControlKeyState = 0x0100
- LEFT_ALT_PRESSED ControlKeyState = 0x0002
- LEFT_CTRL_PRESSED ControlKeyState = 0x0008
- NUMLOCK_ON ControlKeyState = 0x0020
- RIGHT_ALT_PRESSED ControlKeyState = 0x0001
- RIGHT_CTRL_PRESSED ControlKeyState = 0x0004
- SCROLLLOCK_ON ControlKeyState = 0x0040
- SHIFT_PRESSED ControlKeyState = 0x0010
- NO_CONTROL_KEY ControlKeyState = 0x0000
- )
- // String ensures that ControlKeyState satisfies the fmt.Stringer interface.
- func (cks ControlKeyState) String() string {
- controlKeys := []string{}
- switch {
- case cks&CAPSLOCK_ON > 0:
- controlKeys = append(controlKeys, "CapsLock")
- case cks&ENHANCED_KEY > 0:
- controlKeys = append(controlKeys, "Enhanced")
- case cks&LEFT_ALT_PRESSED > 0:
- controlKeys = append(controlKeys, "Alt")
- case cks&LEFT_CTRL_PRESSED > 0:
- controlKeys = append(controlKeys, "CTRL")
- case cks&NUMLOCK_ON > 0:
- controlKeys = append(controlKeys, "NumLock")
- case cks&RIGHT_ALT_PRESSED > 0:
- controlKeys = append(controlKeys, "RightAlt")
- case cks&RIGHT_CTRL_PRESSED > 0:
- controlKeys = append(controlKeys, "RightCTRL")
- case cks&SCROLLLOCK_ON > 0:
- controlKeys = append(controlKeys, "ScrollLock")
- case cks&SHIFT_PRESSED > 0:
- controlKeys = append(controlKeys, "Shift")
- case cks == NO_CONTROL_KEY:
- default:
- return fmt.Sprintf("Unknown(%d)", cks)
- }
- return strings.Join(controlKeys, ",")
- }
- // EventFlags specifies the type of a mouse event (see
- // https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str).
- type EventFlags uint32
- // String ensures that EventFlags satisfies the fmt.Stringer interface.
- func (ef EventFlags) String() string {
- switch {
- case ef&DOUBLE_CLICK > 0:
- return "DoubleClick"
- case ef&MOUSE_WHEELED > 0:
- return "Wheeled"
- case ef&MOUSE_MOVED > 0:
- return "Moved"
- case ef&MOUSE_HWHEELED > 0:
- return "HWheeld"
- case ef == CLICK:
- return "Click"
- default:
- return fmt.Sprintf("Unknown(%d)", ef)
- }
- }
- func (ef EventFlags) Contains(flag EventFlags) bool {
- return ef&flag > 0
- }
- // Valid values for EventFlags.
- const (
- CLICK EventFlags = 0x0000
- MOUSE_MOVED EventFlags = 0x0001
- DOUBLE_CLICK EventFlags = 0x0002
- MOUSE_WHEELED EventFlags = 0x0004
- MOUSE_HWHEELED EventFlags = 0x0008
- )
- func highWord(data uint32) uint16 {
- return uint16((data & 0xFFFF0000) >> 16)
- }
|