ansi_reader.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. //go:build windows
  2. // +build windows
  3. package windowsconsole
  4. import (
  5. "bytes"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "os"
  10. "strings"
  11. "unsafe"
  12. ansiterm "github.com/Azure/go-ansiterm"
  13. "github.com/Azure/go-ansiterm/winterm"
  14. )
  15. const (
  16. escapeSequence = ansiterm.KEY_ESC_CSI
  17. )
  18. // ansiReader wraps a standard input file (e.g., os.Stdin) providing ANSI sequence translation.
  19. type ansiReader struct {
  20. file *os.File
  21. fd uintptr
  22. buffer []byte
  23. cbBuffer int
  24. command []byte
  25. }
  26. // NewAnsiReader returns an io.ReadCloser that provides VT100 terminal emulation on top of a
  27. // Windows console input handle.
  28. func NewAnsiReader(nFile int) io.ReadCloser {
  29. file, fd := winterm.GetStdFile(nFile)
  30. return &ansiReader{
  31. file: file,
  32. fd: fd,
  33. command: make([]byte, 0, ansiterm.ANSI_MAX_CMD_LENGTH),
  34. buffer: make([]byte, 0),
  35. }
  36. }
  37. // Close closes the wrapped file.
  38. func (ar *ansiReader) Close() (err error) {
  39. return ar.file.Close()
  40. }
  41. // Fd returns the file descriptor of the wrapped file.
  42. func (ar *ansiReader) Fd() uintptr {
  43. return ar.fd
  44. }
  45. // Read reads up to len(p) bytes of translated input events into p.
  46. func (ar *ansiReader) Read(p []byte) (int, error) {
  47. if len(p) == 0 {
  48. return 0, nil
  49. }
  50. // Previously read bytes exist, read as much as we can and return
  51. if len(ar.buffer) > 0 {
  52. originalLength := len(ar.buffer)
  53. copiedLength := copy(p, ar.buffer)
  54. if copiedLength == originalLength {
  55. ar.buffer = make([]byte, 0, len(p))
  56. } else {
  57. ar.buffer = ar.buffer[copiedLength:]
  58. }
  59. return copiedLength, nil
  60. }
  61. // Read and translate key events
  62. events, err := readInputEvents(ar, len(p))
  63. if err != nil {
  64. return 0, err
  65. } else if len(events) == 0 {
  66. return 0, nil
  67. }
  68. keyBytes := translateKeyEvents(events, []byte(escapeSequence))
  69. // Save excess bytes and right-size keyBytes
  70. if len(keyBytes) > len(p) {
  71. ar.buffer = keyBytes[len(p):]
  72. keyBytes = keyBytes[:len(p)]
  73. } else if len(keyBytes) == 0 {
  74. return 0, nil
  75. }
  76. copiedLength := copy(p, keyBytes)
  77. if copiedLength != len(keyBytes) {
  78. return 0, errors.New("unexpected copy length encountered")
  79. }
  80. return copiedLength, nil
  81. }
  82. // readInputEvents polls until at least one event is available.
  83. func readInputEvents(ar *ansiReader, maxBytes int) ([]winterm.INPUT_RECORD, error) {
  84. // Determine the maximum number of records to retrieve
  85. // -- Cast around the type system to obtain the size of a single INPUT_RECORD.
  86. // unsafe.Sizeof requires an expression vs. a type-reference; the casting
  87. // tricks the type system into believing it has such an expression.
  88. recordSize := int(unsafe.Sizeof(*((*winterm.INPUT_RECORD)(unsafe.Pointer(&maxBytes)))))
  89. countRecords := maxBytes / recordSize
  90. if countRecords > ansiterm.MAX_INPUT_EVENTS {
  91. countRecords = ansiterm.MAX_INPUT_EVENTS
  92. } else if countRecords == 0 {
  93. countRecords = 1
  94. }
  95. // Wait for and read input events
  96. events := make([]winterm.INPUT_RECORD, countRecords)
  97. nEvents := uint32(0)
  98. eventsExist, err := winterm.WaitForSingleObject(ar.fd, winterm.WAIT_INFINITE)
  99. if err != nil {
  100. return nil, err
  101. }
  102. if eventsExist {
  103. err = winterm.ReadConsoleInput(ar.fd, events, &nEvents)
  104. if err != nil {
  105. return nil, err
  106. }
  107. }
  108. // Return a slice restricted to the number of returned records
  109. return events[:nEvents], nil
  110. }
  111. // KeyEvent Translation Helpers
  112. var arrowKeyMapPrefix = map[uint16]string{
  113. winterm.VK_UP: "%s%sA",
  114. winterm.VK_DOWN: "%s%sB",
  115. winterm.VK_RIGHT: "%s%sC",
  116. winterm.VK_LEFT: "%s%sD",
  117. }
  118. var keyMapPrefix = map[uint16]string{
  119. winterm.VK_UP: "\x1B[%sA",
  120. winterm.VK_DOWN: "\x1B[%sB",
  121. winterm.VK_RIGHT: "\x1B[%sC",
  122. winterm.VK_LEFT: "\x1B[%sD",
  123. winterm.VK_HOME: "\x1B[1%s~", // showkey shows ^[[1
  124. winterm.VK_END: "\x1B[4%s~", // showkey shows ^[[4
  125. winterm.VK_INSERT: "\x1B[2%s~",
  126. winterm.VK_DELETE: "\x1B[3%s~",
  127. winterm.VK_PRIOR: "\x1B[5%s~",
  128. winterm.VK_NEXT: "\x1B[6%s~",
  129. winterm.VK_F1: "",
  130. winterm.VK_F2: "",
  131. winterm.VK_F3: "\x1B[13%s~",
  132. winterm.VK_F4: "\x1B[14%s~",
  133. winterm.VK_F5: "\x1B[15%s~",
  134. winterm.VK_F6: "\x1B[17%s~",
  135. winterm.VK_F7: "\x1B[18%s~",
  136. winterm.VK_F8: "\x1B[19%s~",
  137. winterm.VK_F9: "\x1B[20%s~",
  138. winterm.VK_F10: "\x1B[21%s~",
  139. winterm.VK_F11: "\x1B[23%s~",
  140. winterm.VK_F12: "\x1B[24%s~",
  141. }
  142. // translateKeyEvents converts the input events into the appropriate ANSI string.
  143. func translateKeyEvents(events []winterm.INPUT_RECORD, escapeSequence []byte) []byte {
  144. var buffer bytes.Buffer
  145. for _, event := range events {
  146. if event.EventType == winterm.KEY_EVENT && event.KeyEvent.KeyDown != 0 {
  147. buffer.WriteString(keyToString(&event.KeyEvent, escapeSequence))
  148. }
  149. }
  150. return buffer.Bytes()
  151. }
  152. // keyToString maps the given input event record to the corresponding string.
  153. func keyToString(keyEvent *winterm.KEY_EVENT_RECORD, escapeSequence []byte) string {
  154. if keyEvent.UnicodeChar == 0 {
  155. return formatVirtualKey(keyEvent.VirtualKeyCode, keyEvent.ControlKeyState, escapeSequence)
  156. }
  157. _, alt, control := getControlKeys(keyEvent.ControlKeyState)
  158. if control {
  159. // TODO(azlinux): Implement following control sequences
  160. // <Ctrl>-D Signals the end of input from the keyboard; also exits current shell.
  161. // <Ctrl>-H Deletes the first character to the left of the cursor. Also called the ERASE key.
  162. // <Ctrl>-Q Restarts printing after it has been stopped with <Ctrl>-s.
  163. // <Ctrl>-S Suspends printing on the screen (does not stop the program).
  164. // <Ctrl>-U Deletes all characters on the current line. Also called the KILL key.
  165. // <Ctrl>-E Quits current command and creates a core
  166. }
  167. // <Alt>+Key generates ESC N Key
  168. if !control && alt {
  169. return ansiterm.KEY_ESC_N + strings.ToLower(string(rune(keyEvent.UnicodeChar)))
  170. }
  171. return string(rune(keyEvent.UnicodeChar))
  172. }
  173. // formatVirtualKey converts a virtual key (e.g., up arrow) into the appropriate ANSI string.
  174. func formatVirtualKey(key uint16, controlState uint32, escapeSequence []byte) string {
  175. shift, alt, control := getControlKeys(controlState)
  176. modifier := getControlKeysModifier(shift, alt, control)
  177. if format, ok := arrowKeyMapPrefix[key]; ok {
  178. return fmt.Sprintf(format, escapeSequence, modifier)
  179. }
  180. if format, ok := keyMapPrefix[key]; ok {
  181. return fmt.Sprintf(format, modifier)
  182. }
  183. return ""
  184. }
  185. // getControlKeys extracts the shift, alt, and ctrl key states.
  186. func getControlKeys(controlState uint32) (shift, alt, control bool) {
  187. shift = 0 != (controlState & winterm.SHIFT_PRESSED)
  188. alt = 0 != (controlState & (winterm.LEFT_ALT_PRESSED | winterm.RIGHT_ALT_PRESSED))
  189. control = 0 != (controlState & (winterm.LEFT_CTRL_PRESSED | winterm.RIGHT_CTRL_PRESSED))
  190. return shift, alt, control
  191. }
  192. // getControlKeysModifier returns the ANSI modifier for the given combination of control keys.
  193. func getControlKeysModifier(shift, alt, control bool) string {
  194. if shift && alt && control {
  195. return ansiterm.KEY_CONTROL_PARAM_8
  196. }
  197. if alt && control {
  198. return ansiterm.KEY_CONTROL_PARAM_7
  199. }
  200. if shift && control {
  201. return ansiterm.KEY_CONTROL_PARAM_6
  202. }
  203. if control {
  204. return ansiterm.KEY_CONTROL_PARAM_5
  205. }
  206. if shift && alt {
  207. return ansiterm.KEY_CONTROL_PARAM_4
  208. }
  209. if alt {
  210. return ansiterm.KEY_CONTROL_PARAM_3
  211. }
  212. if shift {
  213. return ansiterm.KEY_CONTROL_PARAM_2
  214. }
  215. return ""
  216. }