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