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