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