main
Raw Download raw file
  1//go:build windows
  2// +build windows
  3
  4package coninput
  5
  6import (
  7	"fmt"
  8	"syscall"
  9	"unsafe"
 10
 11	"golang.org/x/sys/windows"
 12)
 13
 14var (
 15	modkernel32                       = windows.NewLazySystemDLL("kernel32.dll")
 16	procReadConsoleInputW             = modkernel32.NewProc("ReadConsoleInputW")
 17	procPeekConsoleInputW             = modkernel32.NewProc("PeekConsoleInputW")
 18	procGetNumberOfConsoleInputEvents = modkernel32.NewProc("GetNumberOfConsoleInputEvents")
 19	procFlushConsoleInputBuffer       = modkernel32.NewProc("FlushConsoleInputBuffer")
 20)
 21
 22// NewStdinHandle is a shortcut for windows.GetStdHandle(windows.STD_INPUT_HANDLE).
 23func NewStdinHandle() (windows.Handle, error) {
 24	return windows.GetStdHandle(windows.STD_INPUT_HANDLE)
 25}
 26
 27// WinReadConsoleInput is a thin wrapper around the Windows console API function
 28// ReadConsoleInput (see
 29// https://docs.microsoft.com/en-us/windows/console/readconsoleinput). In most
 30// cases it is more practical to either use ReadConsoleInput or
 31// ReadNConsoleInputs.
 32func WinReadConsoleInput(consoleInput windows.Handle, buffer *InputRecord,
 33	length uint32, numberOfEventsRead *uint32) error {
 34	r, _, e := syscall.Syscall6(procReadConsoleInputW.Addr(), 4,
 35		uintptr(consoleInput), uintptr(unsafe.Pointer(buffer)), uintptr(length),
 36		uintptr(unsafe.Pointer(numberOfEventsRead)), 0, 0)
 37	if r == 0 {
 38		return error(e)
 39	}
 40
 41	return nil
 42}
 43
 44// ReadNConsoleInputs is a wrapper around ReadConsoleInput (see
 45// https://docs.microsoft.com/en-us/windows/console/readconsoleinput) that
 46// automates the event buffer allocation in oder to provide io.Reader-like
 47// sematics. maxEvents must be greater than zero.
 48func ReadNConsoleInputs(console windows.Handle, maxEvents uint32) ([]InputRecord, error) {
 49	if maxEvents == 0 {
 50		return nil, fmt.Errorf("maxEvents cannot be zero")
 51	}
 52
 53	var inputRecords = make([]InputRecord, maxEvents)
 54	n, err := ReadConsoleInput(console, inputRecords)
 55
 56	return inputRecords[:n], err
 57}
 58
 59// ReadConsoleInput provides an ideomatic interface to the Windows console API
 60// function ReadConsoleInput (see
 61// https://docs.microsoft.com/en-us/windows/console/readconsoleinput). The size
 62// of inputRecords must be greater than zero.
 63func ReadConsoleInput(console windows.Handle, inputRecords []InputRecord) (uint32, error) {
 64	if len(inputRecords) == 0 {
 65		return 0, fmt.Errorf("size of input record buffer cannot be zero")
 66	}
 67
 68	var read uint32
 69	err := WinReadConsoleInput(console, &inputRecords[0], uint32(len(inputRecords)), &read)
 70
 71	return read, err
 72}
 73
 74// WinPeekConsoleInput is a thin wrapper around the Windows console API function
 75// PeekConsoleInput (see
 76// https://docs.microsoft.com/en-us/windows/console/peekconsoleinput). In most
 77// cases it is more practical to either use PeekConsoleInput or
 78// PeekNConsoleInputs.
 79func WinPeekConsoleInput(consoleInput windows.Handle, buffer *InputRecord,
 80	length uint32, numberOfEventsRead *uint32) error {
 81	r, _, e := syscall.Syscall6(procPeekConsoleInputW.Addr(), 4,
 82		uintptr(consoleInput), uintptr(unsafe.Pointer(buffer)), uintptr(length),
 83		uintptr(unsafe.Pointer(numberOfEventsRead)), 0, 0)
 84	if r == 0 {
 85		return error(e)
 86	}
 87
 88	return nil
 89
 90}
 91
 92// PeekNConsoleInputs is a wrapper around PeekConsoleInput (see
 93// https://docs.microsoft.com/en-us/windows/console/peekconsoleinput) that
 94// automates the event buffer allocation in oder to provide io.Reader-like
 95// sematics. maxEvents must be greater than zero.
 96func PeekNConsoleInputs(console windows.Handle, maxEvents uint32) ([]InputRecord, error) {
 97	if maxEvents == 0 {
 98		return nil, fmt.Errorf("maxEvents cannot be zero")
 99	}
100
101	var inputRecords = make([]InputRecord, maxEvents)
102	n, err := PeekConsoleInput(console, inputRecords)
103
104	return inputRecords[:n], err
105}
106
107// PeekConsoleInput provides an ideomatic interface to the Windows console API
108// function PeekConsoleInput (see
109// https://docs.microsoft.com/en-us/windows/console/peekconsoleinput). The size
110// of inputRecords must be greater than zero.
111func PeekConsoleInput(console windows.Handle, inputRecords []InputRecord) (uint32, error) {
112	if len(inputRecords) == 0 {
113		return 0, fmt.Errorf("size of input record buffer cannot be zero")
114	}
115
116	var read uint32
117
118	err := WinPeekConsoleInput(console, &inputRecords[0], uint32(len(inputRecords)), &read)
119
120	return read, err
121}
122
123// WinGetNumberOfConsoleInputEvents provides an ideomatic interface to the
124// Windows console API function GetNumberOfConsoleInputEvents (see
125// https://docs.microsoft.com/en-us/windows/console/getnumberofconsoleinputevents).
126func WinGetNumberOfConsoleInputEvents(consoleInput windows.Handle, numberOfEvents *uint32) error {
127	r, _, e := syscall.Syscall6(procGetNumberOfConsoleInputEvents.Addr(), 2,
128		uintptr(consoleInput), uintptr(unsafe.Pointer(numberOfEvents)), 0,
129		0, 0, 0)
130	if r == 0 {
131		return error(e)
132	}
133
134	return nil
135}
136
137// GetNumberOfConsoleInputEvents provides an ideomatic interface to the Windows
138// console API function GetNumberOfConsoleInputEvents (see
139// https://docs.microsoft.com/en-us/windows/console/getnumberofconsoleinputevents).
140func GetNumberOfConsoleInputEvents(console windows.Handle) (uint32, error) {
141	var nEvents uint32
142	err := WinGetNumberOfConsoleInputEvents(console, &nEvents)
143
144	return nEvents, err
145}
146
147func FlushConsoleInputBuffer(consoleInput windows.Handle) error {
148	r, _, e := syscall.Syscall(procFlushConsoleInputBuffer.Addr(), 1, uintptr(consoleInput), 0, 0)
149	if r == 0 {
150		return error(e)
151	}
152
153	return nil
154}