read.go 5.3 KB

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