feat: improved deploy progress reporting

See toolshed/abra#478
This commit is contained in:
2025-03-20 14:23:09 +01:00
committed by decentral1se
parent d0f982456e
commit 47045ca8f1
85 changed files with 8828 additions and 360 deletions

15
vendor/github.com/erikgeiser/coninput/.gitignore generated vendored Normal file
View File

@ -0,0 +1,15 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/

24
vendor/github.com/erikgeiser/coninput/.golangci.yml generated vendored Normal file
View File

@ -0,0 +1,24 @@
linters:
enable-all: true
disable:
- golint
- interfacer
- scopelint
- maligned
- rowserrcheck
- funlen
- depguard
- goerr113
- exhaustivestruct
- testpackage
- gochecknoglobals
- wrapcheck
- forbidigo
- ifshort
- cyclop
- gomoddirectives
linters-settings:
exhaustive:
default-signifies-exhaustive: true
issues:
exclude-use-default: false

21
vendor/github.com/erikgeiser/coninput/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Erik G.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

2
vendor/github.com/erikgeiser/coninput/README.md generated vendored Normal file
View File

@ -0,0 +1,2 @@
# coninput
Go library for input handling using Windows Console API

205
vendor/github.com/erikgeiser/coninput/keycodes.go generated vendored Normal file
View File

@ -0,0 +1,205 @@
package coninput
// VirtualKeyCode holds a virtual key code (see
// https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes).
type VirtualKeyCode uint16
const (
VK_LBUTTON VirtualKeyCode = 0x01
VK_RBUTTON VirtualKeyCode = 0x02
VK_CANCEL VirtualKeyCode = 0x03
VK_MBUTTON VirtualKeyCode = 0x04
VK_XBUTTON1 VirtualKeyCode = 0x05
VK_XBUTTON2 VirtualKeyCode = 0x06
VK_BACK VirtualKeyCode = 0x08
VK_TAB VirtualKeyCode = 0x09
VK_CLEAR VirtualKeyCode = 0x0C
VK_RETURN VirtualKeyCode = 0x0D
VK_SHIFT VirtualKeyCode = 0x10
VK_CONTROL VirtualKeyCode = 0x11
VK_MENU VirtualKeyCode = 0x12
VK_PAUSE VirtualKeyCode = 0x13
VK_CAPITAL VirtualKeyCode = 0x14
VK_KANA VirtualKeyCode = 0x15
VK_HANGEUL VirtualKeyCode = 0x15
VK_HANGUL VirtualKeyCode = 0x15
VK_IME_ON VirtualKeyCode = 0x16
VK_JUNJA VirtualKeyCode = 0x17
VK_FINAL VirtualKeyCode = 0x18
VK_HANJA VirtualKeyCode = 0x19
VK_KANJI VirtualKeyCode = 0x19
VK_IME_OFF VirtualKeyCode = 0x1A
VK_ESCAPE VirtualKeyCode = 0x1B
VK_CONVERT VirtualKeyCode = 0x1C
VK_NONCONVERT VirtualKeyCode = 0x1D
VK_ACCEPT VirtualKeyCode = 0x1E
VK_MODECHANGE VirtualKeyCode = 0x1F
VK_SPACE VirtualKeyCode = 0x20
VK_PRIOR VirtualKeyCode = 0x21
VK_NEXT VirtualKeyCode = 0x22
VK_END VirtualKeyCode = 0x23
VK_HOME VirtualKeyCode = 0x24
VK_LEFT VirtualKeyCode = 0x25
VK_UP VirtualKeyCode = 0x26
VK_RIGHT VirtualKeyCode = 0x27
VK_DOWN VirtualKeyCode = 0x28
VK_SELECT VirtualKeyCode = 0x29
VK_PRINT VirtualKeyCode = 0x2A
VK_EXECUTE VirtualKeyCode = 0x2B
VK_SNAPSHOT VirtualKeyCode = 0x2C
VK_INSERT VirtualKeyCode = 0x2D
VK_DELETE VirtualKeyCode = 0x2E
VK_HELP VirtualKeyCode = 0x2F
VK_0 VirtualKeyCode = 0x30
VK_1 VirtualKeyCode = 0x31
VK_2 VirtualKeyCode = 0x32
VK_3 VirtualKeyCode = 0x33
VK_4 VirtualKeyCode = 0x34
VK_5 VirtualKeyCode = 0x35
VK_6 VirtualKeyCode = 0x36
VK_7 VirtualKeyCode = 0x37
VK_8 VirtualKeyCode = 0x38
VK_9 VirtualKeyCode = 0x39
VK_A VirtualKeyCode = 0x41
VK_B VirtualKeyCode = 0x42
VK_C VirtualKeyCode = 0x43
VK_D VirtualKeyCode = 0x44
VK_E VirtualKeyCode = 0x45
VK_F VirtualKeyCode = 0x46
VK_G VirtualKeyCode = 0x47
VK_H VirtualKeyCode = 0x48
VK_I VirtualKeyCode = 0x49
VK_J VirtualKeyCode = 0x4A
VK_K VirtualKeyCode = 0x4B
VK_L VirtualKeyCode = 0x4C
VK_M VirtualKeyCode = 0x4D
VK_N VirtualKeyCode = 0x4E
VK_O VirtualKeyCode = 0x4F
VK_P VirtualKeyCode = 0x50
VK_Q VirtualKeyCode = 0x51
VK_R VirtualKeyCode = 0x52
VK_S VirtualKeyCode = 0x53
VK_T VirtualKeyCode = 0x54
VK_U VirtualKeyCode = 0x55
VK_V VirtualKeyCode = 0x56
VK_W VirtualKeyCode = 0x57
VK_X VirtualKeyCode = 0x58
VK_Y VirtualKeyCode = 0x59
VK_Z VirtualKeyCode = 0x5A
VK_LWIN VirtualKeyCode = 0x5B
VK_RWIN VirtualKeyCode = 0x5C
VK_APPS VirtualKeyCode = 0x5D
VK_SLEEP VirtualKeyCode = 0x5F
VK_NUMPAD0 VirtualKeyCode = 0x60
VK_NUMPAD1 VirtualKeyCode = 0x61
VK_NUMPAD2 VirtualKeyCode = 0x62
VK_NUMPAD3 VirtualKeyCode = 0x63
VK_NUMPAD4 VirtualKeyCode = 0x64
VK_NUMPAD5 VirtualKeyCode = 0x65
VK_NUMPAD6 VirtualKeyCode = 0x66
VK_NUMPAD7 VirtualKeyCode = 0x67
VK_NUMPAD8 VirtualKeyCode = 0x68
VK_NUMPAD9 VirtualKeyCode = 0x69
VK_MULTIPLY VirtualKeyCode = 0x6A
VK_ADD VirtualKeyCode = 0x6B
VK_SEPARATOR VirtualKeyCode = 0x6C
VK_SUBTRACT VirtualKeyCode = 0x6D
VK_DECIMAL VirtualKeyCode = 0x6E
VK_DIVIDE VirtualKeyCode = 0x6F
VK_F1 VirtualKeyCode = 0x70
VK_F2 VirtualKeyCode = 0x71
VK_F3 VirtualKeyCode = 0x72
VK_F4 VirtualKeyCode = 0x73
VK_F5 VirtualKeyCode = 0x74
VK_F6 VirtualKeyCode = 0x75
VK_F7 VirtualKeyCode = 0x76
VK_F8 VirtualKeyCode = 0x77
VK_F9 VirtualKeyCode = 0x78
VK_F10 VirtualKeyCode = 0x79
VK_F11 VirtualKeyCode = 0x7A
VK_F12 VirtualKeyCode = 0x7B
VK_F13 VirtualKeyCode = 0x7C
VK_F14 VirtualKeyCode = 0x7D
VK_F15 VirtualKeyCode = 0x7E
VK_F16 VirtualKeyCode = 0x7F
VK_F17 VirtualKeyCode = 0x80
VK_F18 VirtualKeyCode = 0x81
VK_F19 VirtualKeyCode = 0x82
VK_F20 VirtualKeyCode = 0x83
VK_F21 VirtualKeyCode = 0x84
VK_F22 VirtualKeyCode = 0x85
VK_F23 VirtualKeyCode = 0x86
VK_F24 VirtualKeyCode = 0x87
VK_NUMLOCK VirtualKeyCode = 0x90
VK_SCROLL VirtualKeyCode = 0x91
VK_OEM_NEC_EQUAL VirtualKeyCode = 0x92
VK_OEM_FJ_JISHO VirtualKeyCode = 0x92
VK_OEM_FJ_MASSHOU VirtualKeyCode = 0x93
VK_OEM_FJ_TOUROKU VirtualKeyCode = 0x94
VK_OEM_FJ_LOYA VirtualKeyCode = 0x95
VK_OEM_FJ_ROYA VirtualKeyCode = 0x96
VK_LSHIFT VirtualKeyCode = 0xA0
VK_RSHIFT VirtualKeyCode = 0xA1
VK_LCONTROL VirtualKeyCode = 0xA2
VK_RCONTROL VirtualKeyCode = 0xA3
VK_LMENU VirtualKeyCode = 0xA4
VK_RMENU VirtualKeyCode = 0xA5
VK_BROWSER_BACK VirtualKeyCode = 0xA6
VK_BROWSER_FORWARD VirtualKeyCode = 0xA7
VK_BROWSER_REFRESH VirtualKeyCode = 0xA8
VK_BROWSER_STOP VirtualKeyCode = 0xA9
VK_BROWSER_SEARCH VirtualKeyCode = 0xAA
VK_BROWSER_FAVORITES VirtualKeyCode = 0xAB
VK_BROWSER_HOME VirtualKeyCode = 0xAC
VK_VOLUME_MUTE VirtualKeyCode = 0xAD
VK_VOLUME_DOWN VirtualKeyCode = 0xAE
VK_VOLUME_UP VirtualKeyCode = 0xAF
VK_MEDIA_NEXT_TRACK VirtualKeyCode = 0xB0
VK_MEDIA_PREV_TRACK VirtualKeyCode = 0xB1
VK_MEDIA_STOP VirtualKeyCode = 0xB2
VK_MEDIA_PLAY_PAUSE VirtualKeyCode = 0xB3
VK_LAUNCH_MAIL VirtualKeyCode = 0xB4
VK_LAUNCH_MEDIA_SELECT VirtualKeyCode = 0xB5
VK_LAUNCH_APP1 VirtualKeyCode = 0xB6
VK_LAUNCH_APP2 VirtualKeyCode = 0xB7
VK_OEM_1 VirtualKeyCode = 0xBA
VK_OEM_PLUS VirtualKeyCode = 0xBB
VK_OEM_COMMA VirtualKeyCode = 0xBC
VK_OEM_MINUS VirtualKeyCode = 0xBD
VK_OEM_PERIOD VirtualKeyCode = 0xBE
VK_OEM_2 VirtualKeyCode = 0xBF
VK_OEM_3 VirtualKeyCode = 0xC0
VK_OEM_4 VirtualKeyCode = 0xDB
VK_OEM_5 VirtualKeyCode = 0xDC
VK_OEM_6 VirtualKeyCode = 0xDD
VK_OEM_7 VirtualKeyCode = 0xDE
VK_OEM_8 VirtualKeyCode = 0xDF
VK_OEM_AX VirtualKeyCode = 0xE1
VK_OEM_102 VirtualKeyCode = 0xE2
VK_ICO_HELP VirtualKeyCode = 0xE3
VK_ICO_00 VirtualKeyCode = 0xE4
VK_PROCESSKEY VirtualKeyCode = 0xE5
VK_ICO_CLEAR VirtualKeyCode = 0xE6
VK_OEM_RESET VirtualKeyCode = 0xE9
VK_OEM_JUMP VirtualKeyCode = 0xEA
VK_OEM_PA1 VirtualKeyCode = 0xEB
VK_OEM_PA2 VirtualKeyCode = 0xEC
VK_OEM_PA3 VirtualKeyCode = 0xED
VK_OEM_WSCTRL VirtualKeyCode = 0xEE
VK_OEM_CUSEL VirtualKeyCode = 0xEF
VK_OEM_ATTN VirtualKeyCode = 0xF0
VK_OEM_FINISH VirtualKeyCode = 0xF1
VK_OEM_COPY VirtualKeyCode = 0xF2
VK_OEM_AUTO VirtualKeyCode = 0xF3
VK_OEM_ENLW VirtualKeyCode = 0xF4
VK_OEM_BACKTAB VirtualKeyCode = 0xF5
VK_ATTN VirtualKeyCode = 0xF6
VK_CRSEL VirtualKeyCode = 0xF7
VK_EXSEL VirtualKeyCode = 0xF8
VK_EREOF VirtualKeyCode = 0xF9
VK_PLAY VirtualKeyCode = 0xFA
VK_ZOOM VirtualKeyCode = 0xFB
VK_NONAME VirtualKeyCode = 0xFC
VK_PA1 VirtualKeyCode = 0xFD
VK_OEM_CLEAR VirtualKeyCode = 0xFE
)

82
vendor/github.com/erikgeiser/coninput/mode.go generated vendored Normal file
View File

@ -0,0 +1,82 @@
//go:build windows
// +build windows
package coninput
import (
"strings"
"golang.org/x/sys/windows"
)
// AddInputModes returns the given mode with one or more additional modes enabled.
func AddInputModes(mode uint32, enableModes ...uint32) uint32 {
for _, enableMode := range enableModes {
mode |= enableMode
}
return mode
}
// RemoveInputModes returns the given mode with one or more additional modes disabled.
func RemoveInputModes(mode uint32, disableModes ...uint32) uint32 {
for _, disableMode := range disableModes {
mode &^= disableMode
}
return mode
}
// ToggleInputModes returns the given mode with one or more additional modes toggeled.
func ToggleInputModes(mode uint32, toggleModes ...uint32) uint32 {
for _, toggeMode := range toggleModes {
mode ^= toggeMode
}
return mode
}
var inputModes = []struct {
mode uint32
name string
}{
{mode: windows.ENABLE_ECHO_INPUT, name: "ENABLE_ECHO_INPUT"},
{mode: windows.ENABLE_INSERT_MODE, name: "ENABLE_INSERT_MODE"},
{mode: windows.ENABLE_LINE_INPUT, name: "ENABLE_LINE_INPUT"},
{mode: windows.ENABLE_MOUSE_INPUT, name: "ENABLE_MOUSE_INPUT"},
{mode: windows.ENABLE_PROCESSED_INPUT, name: "ENABLE_PROCESSED_INPUT"},
{mode: windows.ENABLE_QUICK_EDIT_MODE, name: "ENABLE_QUICK_EDIT_MODE"},
{mode: windows.ENABLE_WINDOW_INPUT, name: "ENABLE_WINDOW_INPUT"},
{mode: windows.ENABLE_VIRTUAL_TERMINAL_INPUT, name: "ENABLE_VIRTUAL_TERMINAL_INPUT"},
}
// ListInputMode returnes the isolated enabled input modes as a list.
func ListInputModes(mode uint32) []uint32 {
modes := []uint32{}
for _, inputMode := range inputModes {
if mode&inputMode.mode > 0 {
modes = append(modes, inputMode.mode)
}
}
return modes
}
// ListInputMode returnes the isolated enabled input mode names as a list.
func ListInputModeNames(mode uint32) []string {
modes := []string{}
for _, inputMode := range inputModes {
if mode&inputMode.mode > 0 {
modes = append(modes, inputMode.name)
}
}
return modes
}
// DescribeInputMode returns a string containing the names of each enabled input mode.
func DescribeInputMode(mode uint32) string {
return strings.Join(ListInputModeNames(mode), "|")
}

154
vendor/github.com/erikgeiser/coninput/read.go generated vendored Normal file
View File

@ -0,0 +1,154 @@
//go:build windows
// +build windows
package coninput
import (
"fmt"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var (
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
procReadConsoleInputW = modkernel32.NewProc("ReadConsoleInputW")
procPeekConsoleInputW = modkernel32.NewProc("PeekConsoleInputW")
procGetNumberOfConsoleInputEvents = modkernel32.NewProc("GetNumberOfConsoleInputEvents")
procFlushConsoleInputBuffer = modkernel32.NewProc("FlushConsoleInputBuffer")
)
// NewStdinHandle is a shortcut for windows.GetStdHandle(windows.STD_INPUT_HANDLE).
func NewStdinHandle() (windows.Handle, error) {
return windows.GetStdHandle(windows.STD_INPUT_HANDLE)
}
// WinReadConsoleInput is a thin wrapper around the Windows console API function
// ReadConsoleInput (see
// https://docs.microsoft.com/en-us/windows/console/readconsoleinput). In most
// cases it is more practical to either use ReadConsoleInput or
// ReadNConsoleInputs.
func WinReadConsoleInput(consoleInput windows.Handle, buffer *InputRecord,
length uint32, numberOfEventsRead *uint32) error {
r, _, e := syscall.Syscall6(procReadConsoleInputW.Addr(), 4,
uintptr(consoleInput), uintptr(unsafe.Pointer(buffer)), uintptr(length),
uintptr(unsafe.Pointer(numberOfEventsRead)), 0, 0)
if r == 0 {
return error(e)
}
return nil
}
// ReadNConsoleInputs is a wrapper around ReadConsoleInput (see
// https://docs.microsoft.com/en-us/windows/console/readconsoleinput) that
// automates the event buffer allocation in oder to provide io.Reader-like
// sematics. maxEvents must be greater than zero.
func ReadNConsoleInputs(console windows.Handle, maxEvents uint32) ([]InputRecord, error) {
if maxEvents == 0 {
return nil, fmt.Errorf("maxEvents cannot be zero")
}
var inputRecords = make([]InputRecord, maxEvents)
n, err := ReadConsoleInput(console, inputRecords)
return inputRecords[:n], err
}
// ReadConsoleInput provides an ideomatic interface to the Windows console API
// function ReadConsoleInput (see
// https://docs.microsoft.com/en-us/windows/console/readconsoleinput). The size
// of inputRecords must be greater than zero.
func ReadConsoleInput(console windows.Handle, inputRecords []InputRecord) (uint32, error) {
if len(inputRecords) == 0 {
return 0, fmt.Errorf("size of input record buffer cannot be zero")
}
var read uint32
err := WinReadConsoleInput(console, &inputRecords[0], uint32(len(inputRecords)), &read)
return read, err
}
// WinPeekConsoleInput is a thin wrapper around the Windows console API function
// PeekConsoleInput (see
// https://docs.microsoft.com/en-us/windows/console/peekconsoleinput). In most
// cases it is more practical to either use PeekConsoleInput or
// PeekNConsoleInputs.
func WinPeekConsoleInput(consoleInput windows.Handle, buffer *InputRecord,
length uint32, numberOfEventsRead *uint32) error {
r, _, e := syscall.Syscall6(procPeekConsoleInputW.Addr(), 4,
uintptr(consoleInput), uintptr(unsafe.Pointer(buffer)), uintptr(length),
uintptr(unsafe.Pointer(numberOfEventsRead)), 0, 0)
if r == 0 {
return error(e)
}
return nil
}
// PeekNConsoleInputs is a wrapper around PeekConsoleInput (see
// https://docs.microsoft.com/en-us/windows/console/peekconsoleinput) that
// automates the event buffer allocation in oder to provide io.Reader-like
// sematics. maxEvents must be greater than zero.
func PeekNConsoleInputs(console windows.Handle, maxEvents uint32) ([]InputRecord, error) {
if maxEvents == 0 {
return nil, fmt.Errorf("maxEvents cannot be zero")
}
var inputRecords = make([]InputRecord, maxEvents)
n, err := PeekConsoleInput(console, inputRecords)
return inputRecords[:n], err
}
// PeekConsoleInput provides an ideomatic interface to the Windows console API
// function PeekConsoleInput (see
// https://docs.microsoft.com/en-us/windows/console/peekconsoleinput). The size
// of inputRecords must be greater than zero.
func PeekConsoleInput(console windows.Handle, inputRecords []InputRecord) (uint32, error) {
if len(inputRecords) == 0 {
return 0, fmt.Errorf("size of input record buffer cannot be zero")
}
var read uint32
err := WinPeekConsoleInput(console, &inputRecords[0], uint32(len(inputRecords)), &read)
return read, err
}
// WinGetNumberOfConsoleInputEvents provides an ideomatic interface to the
// Windows console API function GetNumberOfConsoleInputEvents (see
// https://docs.microsoft.com/en-us/windows/console/getnumberofconsoleinputevents).
func WinGetNumberOfConsoleInputEvents(consoleInput windows.Handle, numberOfEvents *uint32) error {
r, _, e := syscall.Syscall6(procGetNumberOfConsoleInputEvents.Addr(), 2,
uintptr(consoleInput), uintptr(unsafe.Pointer(numberOfEvents)), 0,
0, 0, 0)
if r == 0 {
return error(e)
}
return nil
}
// GetNumberOfConsoleInputEvents provides an ideomatic interface to the Windows
// console API function GetNumberOfConsoleInputEvents (see
// https://docs.microsoft.com/en-us/windows/console/getnumberofconsoleinputevents).
func GetNumberOfConsoleInputEvents(console windows.Handle) (uint32, error) {
var nEvents uint32
err := WinGetNumberOfConsoleInputEvents(console, &nEvents)
return nEvents, err
}
func FlushConsoleInputBuffer(consoleInput windows.Handle) error {
r, _, e := syscall.Syscall(procFlushConsoleInputBuffer.Addr(), 1, uintptr(consoleInput), 0, 0)
if r == 0 {
return error(e)
}
return nil
}

486
vendor/github.com/erikgeiser/coninput/records.go generated vendored Normal file
View File

@ -0,0 +1,486 @@
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)
}