github.com/carlanton/docker@v1.8.0-rc1/pkg/term/winconsole/console_windows.go (about) 1 // +build windows 2 3 package winconsole 4 5 import ( 6 "bytes" 7 "fmt" 8 "io" 9 "os" 10 "strconv" 11 "strings" 12 "sync" 13 "syscall" 14 "unsafe" 15 16 "github.com/Sirupsen/logrus" 17 ) 18 19 const ( 20 // Consts for Get/SetConsoleMode function 21 // -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx 22 ENABLE_PROCESSED_INPUT = 0x0001 23 ENABLE_LINE_INPUT = 0x0002 24 ENABLE_ECHO_INPUT = 0x0004 25 ENABLE_WINDOW_INPUT = 0x0008 26 ENABLE_MOUSE_INPUT = 0x0010 27 ENABLE_INSERT_MODE = 0x0020 28 ENABLE_QUICK_EDIT_MODE = 0x0040 29 ENABLE_EXTENDED_FLAGS = 0x0080 30 31 // If parameter is a screen buffer handle, additional values 32 ENABLE_PROCESSED_OUTPUT = 0x0001 33 ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002 34 35 //http://msdn.microsoft.com/en-us/library/windows/desktop/ms682088(v=vs.85).aspx#_win32_character_attributes 36 FOREGROUND_BLUE = 1 37 FOREGROUND_GREEN = 2 38 FOREGROUND_RED = 4 39 FOREGROUND_INTENSITY = 8 40 FOREGROUND_MASK_SET = 0x000F 41 FOREGROUND_MASK_UNSET = 0xFFF0 42 43 BACKGROUND_BLUE = 16 44 BACKGROUND_GREEN = 32 45 BACKGROUND_RED = 64 46 BACKGROUND_INTENSITY = 128 47 BACKGROUND_MASK_SET = 0x00F0 48 BACKGROUND_MASK_UNSET = 0xFF0F 49 50 COMMON_LVB_REVERSE_VIDEO = 0x4000 51 COMMON_LVB_UNDERSCORE = 0x8000 52 53 // http://man7.org/linux/man-pages/man4/console_codes.4.html 54 // ECMA-48 Set Graphics Rendition 55 ANSI_ATTR_RESET = 0 56 ANSI_ATTR_BOLD = 1 57 ANSI_ATTR_DIM = 2 58 ANSI_ATTR_UNDERLINE = 4 59 ANSI_ATTR_BLINK = 5 60 ANSI_ATTR_REVERSE = 7 61 ANSI_ATTR_INVISIBLE = 8 62 63 ANSI_ATTR_UNDERLINE_OFF = 24 64 ANSI_ATTR_BLINK_OFF = 25 65 ANSI_ATTR_REVERSE_OFF = 27 66 ANSI_ATTR_INVISIBLE_OFF = 8 67 68 ANSI_FOREGROUND_BLACK = 30 69 ANSI_FOREGROUND_RED = 31 70 ANSI_FOREGROUND_GREEN = 32 71 ANSI_FOREGROUND_YELLOW = 33 72 ANSI_FOREGROUND_BLUE = 34 73 ANSI_FOREGROUND_MAGENTA = 35 74 ANSI_FOREGROUND_CYAN = 36 75 ANSI_FOREGROUND_WHITE = 37 76 ANSI_FOREGROUND_DEFAULT = 39 77 78 ANSI_BACKGROUND_BLACK = 40 79 ANSI_BACKGROUND_RED = 41 80 ANSI_BACKGROUND_GREEN = 42 81 ANSI_BACKGROUND_YELLOW = 43 82 ANSI_BACKGROUND_BLUE = 44 83 ANSI_BACKGROUND_MAGENTA = 45 84 ANSI_BACKGROUND_CYAN = 46 85 ANSI_BACKGROUND_WHITE = 47 86 ANSI_BACKGROUND_DEFAULT = 49 87 88 ANSI_MAX_CMD_LENGTH = 256 89 90 MAX_INPUT_EVENTS = 128 91 MAX_INPUT_BUFFER = 1024 92 DEFAULT_WIDTH = 80 93 DEFAULT_HEIGHT = 24 94 ) 95 96 // http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx 97 const ( 98 VK_PRIOR = 0x21 // PAGE UP key 99 VK_NEXT = 0x22 // PAGE DOWN key 100 VK_END = 0x23 // END key 101 VK_HOME = 0x24 // HOME key 102 VK_LEFT = 0x25 // LEFT ARROW key 103 VK_UP = 0x26 // UP ARROW key 104 VK_RIGHT = 0x27 // RIGHT ARROW key 105 VK_DOWN = 0x28 // DOWN ARROW key 106 VK_SELECT = 0x29 // SELECT key 107 VK_PRINT = 0x2A // PRINT key 108 VK_EXECUTE = 0x2B // EXECUTE key 109 VK_SNAPSHOT = 0x2C // PRINT SCREEN key 110 VK_INSERT = 0x2D // INS key 111 VK_DELETE = 0x2E // DEL key 112 VK_HELP = 0x2F // HELP key 113 VK_F1 = 0x70 // F1 key 114 VK_F2 = 0x71 // F2 key 115 VK_F3 = 0x72 // F3 key 116 VK_F4 = 0x73 // F4 key 117 VK_F5 = 0x74 // F5 key 118 VK_F6 = 0x75 // F6 key 119 VK_F7 = 0x76 // F7 key 120 VK_F8 = 0x77 // F8 key 121 VK_F9 = 0x78 // F9 key 122 VK_F10 = 0x79 // F10 key 123 VK_F11 = 0x7A // F11 key 124 VK_F12 = 0x7B // F12 key 125 ) 126 127 var kernel32DLL = syscall.NewLazyDLL("kernel32.dll") 128 129 var ( 130 setConsoleModeProc = kernel32DLL.NewProc("SetConsoleMode") 131 getConsoleScreenBufferInfoProc = kernel32DLL.NewProc("GetConsoleScreenBufferInfo") 132 setConsoleCursorPositionProc = kernel32DLL.NewProc("SetConsoleCursorPosition") 133 setConsoleTextAttributeProc = kernel32DLL.NewProc("SetConsoleTextAttribute") 134 fillConsoleOutputCharacterProc = kernel32DLL.NewProc("FillConsoleOutputCharacterW") 135 writeConsoleOutputProc = kernel32DLL.NewProc("WriteConsoleOutputW") 136 readConsoleInputProc = kernel32DLL.NewProc("ReadConsoleInputW") 137 getNumberOfConsoleInputEventsProc = kernel32DLL.NewProc("GetNumberOfConsoleInputEvents") 138 getConsoleCursorInfoProc = kernel32DLL.NewProc("GetConsoleCursorInfo") 139 setConsoleCursorInfoProc = kernel32DLL.NewProc("SetConsoleCursorInfo") 140 setConsoleWindowInfoProc = kernel32DLL.NewProc("SetConsoleWindowInfo") 141 setConsoleScreenBufferSizeProc = kernel32DLL.NewProc("SetConsoleScreenBufferSize") 142 ) 143 144 // types for calling various windows API 145 // see http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093(v=vs.85).aspx 146 type ( 147 SHORT int16 148 BOOL int32 149 WORD uint16 150 WCHAR uint16 151 DWORD uint32 152 153 SMALL_RECT struct { 154 Left SHORT 155 Top SHORT 156 Right SHORT 157 Bottom SHORT 158 } 159 160 COORD struct { 161 X SHORT 162 Y SHORT 163 } 164 165 CONSOLE_SCREEN_BUFFER_INFO struct { 166 Size COORD 167 CursorPosition COORD 168 Attributes WORD 169 Window SMALL_RECT 170 MaximumWindowSize COORD 171 } 172 173 CONSOLE_CURSOR_INFO struct { 174 Size DWORD 175 Visible BOOL 176 } 177 178 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms684166(v=vs.85).aspx 179 KEY_EVENT_RECORD struct { 180 KeyDown BOOL 181 RepeatCount WORD 182 VirtualKeyCode WORD 183 VirtualScanCode WORD 184 UnicodeChar WCHAR 185 ControlKeyState DWORD 186 } 187 188 INPUT_RECORD struct { 189 EventType WORD 190 KeyEvent KEY_EVENT_RECORD 191 } 192 193 CHAR_INFO struct { 194 UnicodeChar WCHAR 195 Attributes WORD 196 } 197 ) 198 199 // TODO(azlinux): Basic type clean-up 200 // -- Convert all uses of uintptr to syscall.Handle to be consistent with Windows syscall 201 // -- Convert, as appropriate, types to use defined Windows types (e.g., DWORD instead of uint32) 202 203 // Implements the TerminalEmulator interface 204 type WindowsTerminal struct { 205 outMutex sync.Mutex 206 inMutex sync.Mutex 207 inputBuffer []byte 208 inputSize int 209 inputEvents []INPUT_RECORD 210 screenBufferInfo *CONSOLE_SCREEN_BUFFER_INFO 211 inputEscapeSequence []byte 212 } 213 214 func getStdHandle(stdhandle int) uintptr { 215 handle, err := syscall.GetStdHandle(stdhandle) 216 if err != nil { 217 panic(fmt.Errorf("could not get standard io handle %d", stdhandle)) 218 } 219 return uintptr(handle) 220 } 221 222 func WinConsoleStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { 223 handler := &WindowsTerminal{ 224 inputBuffer: make([]byte, MAX_INPUT_BUFFER), 225 inputEscapeSequence: []byte(KEY_ESC_CSI), 226 inputEvents: make([]INPUT_RECORD, MAX_INPUT_EVENTS), 227 } 228 229 if IsConsole(os.Stdin.Fd()) { 230 stdIn = &terminalReader{ 231 wrappedReader: os.Stdin, 232 emulator: handler, 233 command: make([]byte, 0, ANSI_MAX_CMD_LENGTH), 234 fd: getStdHandle(syscall.STD_INPUT_HANDLE), 235 } 236 } else { 237 stdIn = os.Stdin 238 } 239 240 if IsConsole(os.Stdout.Fd()) { 241 stdoutHandle := getStdHandle(syscall.STD_OUTPUT_HANDLE) 242 243 // Save current screen buffer info 244 screenBufferInfo, err := GetConsoleScreenBufferInfo(stdoutHandle) 245 if err != nil { 246 // If GetConsoleScreenBufferInfo returns a nil error, it usually means that stdout is not a TTY. 247 // However, this is in the branch where stdout is a TTY, hence the panic. 248 panic("could not get console screen buffer info") 249 } 250 handler.screenBufferInfo = screenBufferInfo 251 252 buffer = make([]CHAR_INFO, screenBufferInfo.MaximumWindowSize.X*screenBufferInfo.MaximumWindowSize.Y) 253 254 stdOut = &terminalWriter{ 255 wrappedWriter: os.Stdout, 256 emulator: handler, 257 command: make([]byte, 0, ANSI_MAX_CMD_LENGTH), 258 fd: stdoutHandle, 259 } 260 } else { 261 stdOut = os.Stdout 262 } 263 264 if IsConsole(os.Stderr.Fd()) { 265 stdErr = &terminalWriter{ 266 wrappedWriter: os.Stderr, 267 emulator: handler, 268 command: make([]byte, 0, ANSI_MAX_CMD_LENGTH), 269 fd: getStdHandle(syscall.STD_ERROR_HANDLE), 270 } 271 } else { 272 stdErr = os.Stderr 273 } 274 275 return stdIn, stdOut, stdErr 276 } 277 278 // GetHandleInfo returns file descriptor and bool indicating whether the file is a console. 279 func GetHandleInfo(in interface{}) (uintptr, bool) { 280 var inFd uintptr 281 var isTerminalIn bool 282 283 switch t := in.(type) { 284 case *terminalReader: 285 in = t.wrappedReader 286 case *terminalWriter: 287 in = t.wrappedWriter 288 } 289 290 if file, ok := in.(*os.File); ok { 291 inFd = file.Fd() 292 isTerminalIn = IsConsole(inFd) 293 } 294 return inFd, isTerminalIn 295 } 296 297 func getError(r1, r2 uintptr, lastErr error) error { 298 // If the function fails, the return value is zero. 299 if r1 == 0 { 300 if lastErr != nil { 301 return lastErr 302 } 303 return syscall.EINVAL 304 } 305 return nil 306 } 307 308 // GetConsoleMode gets the console mode for given file descriptor 309 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx 310 func GetConsoleMode(handle uintptr) (uint32, error) { 311 var mode uint32 312 err := syscall.GetConsoleMode(syscall.Handle(handle), &mode) 313 return mode, err 314 } 315 316 // SetConsoleMode sets the console mode for given file descriptor 317 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx 318 func SetConsoleMode(handle uintptr, mode uint32) error { 319 return getError(setConsoleModeProc.Call(handle, uintptr(mode), 0)) 320 } 321 322 // SetCursorVisible sets the cursor visbility 323 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms686019(v=vs.85).aspx 324 func SetCursorVisible(handle uintptr, isVisible BOOL) (bool, error) { 325 var cursorInfo *CONSOLE_CURSOR_INFO = &CONSOLE_CURSOR_INFO{} 326 if err := getError(getConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0)); err != nil { 327 return false, err 328 } 329 cursorInfo.Visible = isVisible 330 if err := getError(setConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0)); err != nil { 331 return false, err 332 } 333 return true, nil 334 } 335 336 // SetWindowSize sets the size of the console window. 337 func SetWindowSize(handle uintptr, width, height, max SHORT) (bool, error) { 338 window := SMALL_RECT{Left: 0, Top: 0, Right: width - 1, Bottom: height - 1} 339 coord := COORD{X: width - 1, Y: max} 340 if err := getError(setConsoleWindowInfoProc.Call(handle, uintptr(1), uintptr(unsafe.Pointer(&window)))); err != nil { 341 return false, err 342 } 343 if err := getError(setConsoleScreenBufferSizeProc.Call(handle, marshal(coord))); err != nil { 344 return false, err 345 } 346 return true, nil 347 } 348 349 // GetConsoleScreenBufferInfo retrieves information about the specified console screen buffer. 350 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx 351 func GetConsoleScreenBufferInfo(handle uintptr) (*CONSOLE_SCREEN_BUFFER_INFO, error) { 352 var info CONSOLE_SCREEN_BUFFER_INFO 353 if err := getError(getConsoleScreenBufferInfoProc.Call(handle, uintptr(unsafe.Pointer(&info)), 0)); err != nil { 354 return nil, err 355 } 356 return &info, nil 357 } 358 359 // setConsoleTextAttribute sets the attributes of characters written to the 360 // console screen buffer by the WriteFile or WriteConsole function, 361 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms686047(v=vs.85).aspx 362 func setConsoleTextAttribute(handle uintptr, attribute WORD) error { 363 return getError(setConsoleTextAttributeProc.Call(handle, uintptr(attribute), 0)) 364 } 365 366 func writeConsoleOutput(handle uintptr, buffer []CHAR_INFO, bufferSize COORD, bufferCoord COORD, writeRegion *SMALL_RECT) (bool, error) { 367 if err := getError(writeConsoleOutputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), marshal(bufferSize), marshal(bufferCoord), uintptr(unsafe.Pointer(writeRegion)))); err != nil { 368 return false, err 369 } 370 return true, nil 371 } 372 373 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682663(v=vs.85).aspx 374 func fillConsoleOutputCharacter(handle uintptr, fillChar byte, length uint32, writeCord COORD) (bool, error) { 375 out := int64(0) 376 if err := getError(fillConsoleOutputCharacterProc.Call(handle, uintptr(fillChar), uintptr(length), marshal(writeCord), uintptr(unsafe.Pointer(&out)))); err != nil { 377 return false, err 378 } 379 return true, nil 380 } 381 382 // Gets the number of space characters to write for "clearing" the section of terminal 383 func getNumberOfChars(fromCoord COORD, toCoord COORD, screenSize COORD) uint32 { 384 // must be valid cursor position 385 if fromCoord.X < 0 || fromCoord.Y < 0 || toCoord.X < 0 || toCoord.Y < 0 { 386 return 0 387 } 388 if fromCoord.X >= screenSize.X || fromCoord.Y >= screenSize.Y || toCoord.X >= screenSize.X || toCoord.Y >= screenSize.Y { 389 return 0 390 } 391 // can't be backwards 392 if fromCoord.Y > toCoord.Y { 393 return 0 394 } 395 // same line 396 if fromCoord.Y == toCoord.Y { 397 return uint32(toCoord.X-fromCoord.X) + 1 398 } 399 // spans more than one line 400 if fromCoord.Y < toCoord.Y { 401 // from start till end of line for first line + from start of line till end 402 retValue := uint32(screenSize.X-fromCoord.X) + uint32(toCoord.X) + 1 403 // don't count first and last line 404 linesBetween := toCoord.Y - fromCoord.Y - 1 405 if linesBetween > 0 { 406 retValue = retValue + uint32(linesBetween*screenSize.X) 407 } 408 return retValue 409 } 410 return 0 411 } 412 413 var buffer []CHAR_INFO 414 415 func clearDisplayRect(handle uintptr, attributes WORD, fromCoord COORD, toCoord COORD) (uint32, error) { 416 var writeRegion SMALL_RECT 417 writeRegion.Left = fromCoord.X 418 writeRegion.Top = fromCoord.Y 419 writeRegion.Right = toCoord.X 420 writeRegion.Bottom = toCoord.Y 421 422 // allocate and initialize buffer 423 width := toCoord.X - fromCoord.X + 1 424 height := toCoord.Y - fromCoord.Y + 1 425 size := uint32(width) * uint32(height) 426 if size > 0 { 427 buffer := make([]CHAR_INFO, size) 428 for i := range buffer { 429 buffer[i] = CHAR_INFO{WCHAR(' '), attributes} 430 } 431 432 // Write to buffer 433 r, err := writeConsoleOutput(handle, buffer, COORD{X: width, Y: height}, COORD{X: 0, Y: 0}, &writeRegion) 434 if !r { 435 if err != nil { 436 return 0, err 437 } 438 return 0, syscall.EINVAL 439 } 440 } 441 return uint32(size), nil 442 } 443 444 func clearDisplayRange(handle uintptr, attributes WORD, fromCoord COORD, toCoord COORD) (uint32, error) { 445 nw := uint32(0) 446 // start and end on same line 447 if fromCoord.Y == toCoord.Y { 448 return clearDisplayRect(handle, attributes, fromCoord, toCoord) 449 } 450 // TODO(azlinux): if full screen, optimize 451 452 // spans more than one line 453 if fromCoord.Y < toCoord.Y { 454 // from start position till end of line for first line 455 n, err := clearDisplayRect(handle, attributes, fromCoord, COORD{X: toCoord.X, Y: fromCoord.Y}) 456 if err != nil { 457 return nw, err 458 } 459 nw += n 460 // lines between 461 linesBetween := toCoord.Y - fromCoord.Y - 1 462 if linesBetween > 0 { 463 n, err = clearDisplayRect(handle, attributes, COORD{X: 0, Y: fromCoord.Y + 1}, COORD{X: toCoord.X, Y: toCoord.Y - 1}) 464 if err != nil { 465 return nw, err 466 } 467 nw += n 468 } 469 // lines at end 470 n, err = clearDisplayRect(handle, attributes, COORD{X: 0, Y: toCoord.Y}, toCoord) 471 if err != nil { 472 return nw, err 473 } 474 nw += n 475 } 476 return nw, nil 477 } 478 479 // setConsoleCursorPosition sets the console cursor position 480 // Note The X and Y are zero based 481 // If relative is true then the new position is relative to current one 482 func setConsoleCursorPosition(handle uintptr, isRelative bool, column int16, line int16) error { 483 screenBufferInfo, err := GetConsoleScreenBufferInfo(handle) 484 if err != nil { 485 return err 486 } 487 var position COORD 488 if isRelative { 489 position.X = screenBufferInfo.CursorPosition.X + SHORT(column) 490 position.Y = screenBufferInfo.CursorPosition.Y + SHORT(line) 491 } else { 492 position.X = SHORT(column) 493 position.Y = SHORT(line) 494 } 495 return getError(setConsoleCursorPositionProc.Call(handle, marshal(position), 0)) 496 } 497 498 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683207(v=vs.85).aspx 499 func getNumberOfConsoleInputEvents(handle uintptr) (uint16, error) { 500 var n DWORD 501 if err := getError(getNumberOfConsoleInputEventsProc.Call(handle, uintptr(unsafe.Pointer(&n)))); err != nil { 502 return 0, err 503 } 504 return uint16(n), nil 505 } 506 507 //http://msdn.microsoft.com/en-us/library/windows/desktop/ms684961(v=vs.85).aspx 508 func readConsoleInputKey(handle uintptr, inputBuffer []INPUT_RECORD) (int, error) { 509 var nr DWORD 510 if err := getError(readConsoleInputProc.Call(handle, uintptr(unsafe.Pointer(&inputBuffer[0])), uintptr(len(inputBuffer)), uintptr(unsafe.Pointer(&nr)))); err != nil { 511 return 0, err 512 } 513 return int(nr), nil 514 } 515 516 func getWindowsTextAttributeForAnsiValue(originalFlag WORD, defaultValue WORD, ansiValue int16) (WORD, error) { 517 flag := WORD(originalFlag) 518 if flag == 0 { 519 flag = defaultValue 520 } 521 switch ansiValue { 522 case ANSI_ATTR_RESET: 523 flag &^= COMMON_LVB_UNDERSCORE 524 flag &^= BACKGROUND_INTENSITY 525 flag = flag | FOREGROUND_INTENSITY 526 case ANSI_ATTR_INVISIBLE: 527 // TODO: how do you reset reverse? 528 case ANSI_ATTR_UNDERLINE: 529 flag = flag | COMMON_LVB_UNDERSCORE 530 case ANSI_ATTR_BLINK: 531 // seems like background intenisty is blink 532 flag = flag | BACKGROUND_INTENSITY 533 case ANSI_ATTR_UNDERLINE_OFF: 534 flag &^= COMMON_LVB_UNDERSCORE 535 case ANSI_ATTR_BLINK_OFF: 536 // seems like background intenisty is blink 537 flag &^= BACKGROUND_INTENSITY 538 case ANSI_ATTR_BOLD: 539 flag = flag | FOREGROUND_INTENSITY 540 case ANSI_ATTR_DIM: 541 flag &^= FOREGROUND_INTENSITY 542 case ANSI_ATTR_REVERSE, ANSI_ATTR_REVERSE_OFF: 543 // swap forground and background bits 544 foreground := flag & FOREGROUND_MASK_SET 545 background := flag & BACKGROUND_MASK_SET 546 flag = (flag & BACKGROUND_MASK_UNSET & FOREGROUND_MASK_UNSET) | (foreground << 4) | (background >> 4) 547 548 // FOREGROUND 549 case ANSI_FOREGROUND_DEFAULT: 550 flag = (flag & FOREGROUND_MASK_UNSET) | (defaultValue & FOREGROUND_MASK_SET) 551 case ANSI_FOREGROUND_BLACK: 552 flag = flag ^ (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE) 553 case ANSI_FOREGROUND_RED: 554 flag = (flag & FOREGROUND_MASK_UNSET) | FOREGROUND_RED 555 case ANSI_FOREGROUND_GREEN: 556 flag = (flag & FOREGROUND_MASK_UNSET) | FOREGROUND_GREEN 557 case ANSI_FOREGROUND_YELLOW: 558 flag = (flag & FOREGROUND_MASK_UNSET) | FOREGROUND_RED | FOREGROUND_GREEN 559 case ANSI_FOREGROUND_BLUE: 560 flag = (flag & FOREGROUND_MASK_UNSET) | FOREGROUND_BLUE 561 case ANSI_FOREGROUND_MAGENTA: 562 flag = (flag & FOREGROUND_MASK_UNSET) | FOREGROUND_RED | FOREGROUND_BLUE 563 case ANSI_FOREGROUND_CYAN: 564 flag = (flag & FOREGROUND_MASK_UNSET) | FOREGROUND_GREEN | FOREGROUND_BLUE 565 case ANSI_FOREGROUND_WHITE: 566 flag = (flag & FOREGROUND_MASK_UNSET) | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE 567 568 // Background 569 case ANSI_BACKGROUND_DEFAULT: 570 // Black with no intensity 571 flag = (flag & BACKGROUND_MASK_UNSET) | (defaultValue & BACKGROUND_MASK_SET) 572 case ANSI_BACKGROUND_BLACK: 573 flag = (flag & BACKGROUND_MASK_UNSET) 574 case ANSI_BACKGROUND_RED: 575 flag = (flag & BACKGROUND_MASK_UNSET) | BACKGROUND_RED 576 case ANSI_BACKGROUND_GREEN: 577 flag = (flag & BACKGROUND_MASK_UNSET) | BACKGROUND_GREEN 578 case ANSI_BACKGROUND_YELLOW: 579 flag = (flag & BACKGROUND_MASK_UNSET) | BACKGROUND_RED | BACKGROUND_GREEN 580 case ANSI_BACKGROUND_BLUE: 581 flag = (flag & BACKGROUND_MASK_UNSET) | BACKGROUND_BLUE 582 case ANSI_BACKGROUND_MAGENTA: 583 flag = (flag & BACKGROUND_MASK_UNSET) | BACKGROUND_RED | BACKGROUND_BLUE 584 case ANSI_BACKGROUND_CYAN: 585 flag = (flag & BACKGROUND_MASK_UNSET) | BACKGROUND_GREEN | BACKGROUND_BLUE 586 case ANSI_BACKGROUND_WHITE: 587 flag = (flag & BACKGROUND_MASK_UNSET) | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE 588 } 589 return flag, nil 590 } 591 592 // HandleOutputCommand interpretes the Ansi commands and then makes appropriate Win32 calls 593 func (term *WindowsTerminal) HandleOutputCommand(handle uintptr, command []byte) (n int, err error) { 594 // always consider all the bytes in command, processed 595 n = len(command) 596 597 parsedCommand := parseAnsiCommand(command) 598 logrus.Debugf("[windows] HandleOutputCommand: %v", parsedCommand) 599 600 // console settings changes need to happen in atomic way 601 term.outMutex.Lock() 602 defer term.outMutex.Unlock() 603 604 switch parsedCommand.Command { 605 case "m": 606 // [Value;...;Valuem 607 // Set Graphics Mode: 608 // Calls the graphics functions specified by the following values. 609 // These specified functions remain active until the next occurrence of this escape sequence. 610 // Graphics mode changes the colors and attributes of text (such as bold and underline) displayed on the screen. 611 screenBufferInfo, err := GetConsoleScreenBufferInfo(handle) 612 if err != nil { 613 return n, err 614 } 615 flag := screenBufferInfo.Attributes 616 for _, e := range parsedCommand.Parameters { 617 value, _ := strconv.ParseInt(e, 10, 16) // base 10, 16 bit 618 if value == ANSI_ATTR_RESET { 619 flag = term.screenBufferInfo.Attributes // reset 620 } else { 621 flag, err = getWindowsTextAttributeForAnsiValue(flag, term.screenBufferInfo.Attributes, int16(value)) 622 if err != nil { 623 return n, err 624 } 625 } 626 } 627 if err := setConsoleTextAttribute(handle, flag); err != nil { 628 return n, err 629 } 630 case "H", "f": 631 // [line;columnH 632 // [line;columnf 633 // Moves the cursor to the specified position (coordinates). 634 // If you do not specify a position, the cursor moves to the home position at the upper-left corner of the screen (line 0, column 0). 635 screenBufferInfo, err := GetConsoleScreenBufferInfo(handle) 636 if err != nil { 637 return n, err 638 } 639 line, err := parseInt16OrDefault(parsedCommand.getParam(0), 1) 640 if err != nil { 641 return n, err 642 } 643 if line > int16(screenBufferInfo.Window.Bottom) { 644 line = int16(screenBufferInfo.Window.Bottom) + 1 645 } 646 column, err := parseInt16OrDefault(parsedCommand.getParam(1), 1) 647 if err != nil { 648 return n, err 649 } 650 if column > int16(screenBufferInfo.Window.Right) { 651 column = int16(screenBufferInfo.Window.Right) + 1 652 } 653 // The numbers are not 0 based, but 1 based 654 logrus.Debugf("[windows] HandleOutputCommmand: Moving cursor to (%v,%v)", column-1, line-1) 655 if err := setConsoleCursorPosition(handle, false, column-1, line-1); err != nil { 656 return n, err 657 } 658 659 case "A": 660 // [valueA 661 // Moves the cursor up by the specified number of lines without changing columns. 662 // If the cursor is already on the top line, ignores this sequence. 663 value, err := parseInt16OrDefault(parsedCommand.getParam(0), 1) 664 if err != nil { 665 return len(command), err 666 } 667 if err := setConsoleCursorPosition(handle, true, 0, -value); err != nil { 668 return n, err 669 } 670 case "B": 671 // [valueB 672 // Moves the cursor down by the specified number of lines without changing columns. 673 // If the cursor is already on the bottom line, ignores this sequence. 674 value, err := parseInt16OrDefault(parsedCommand.getParam(0), 1) 675 if err != nil { 676 return n, err 677 } 678 if err := setConsoleCursorPosition(handle, true, 0, value); err != nil { 679 return n, err 680 } 681 case "C": 682 // [valueC 683 // Moves the cursor forward by the specified number of columns without changing lines. 684 // If the cursor is already in the rightmost column, ignores this sequence. 685 value, err := parseInt16OrDefault(parsedCommand.getParam(0), 1) 686 if err != nil { 687 return n, err 688 } 689 if err := setConsoleCursorPosition(handle, true, value, 0); err != nil { 690 return n, err 691 } 692 case "D": 693 // [valueD 694 // Moves the cursor back by the specified number of columns without changing lines. 695 // If the cursor is already in the leftmost column, ignores this sequence. 696 value, err := parseInt16OrDefault(parsedCommand.getParam(0), 1) 697 if err != nil { 698 return n, err 699 } 700 if err := setConsoleCursorPosition(handle, true, -value, 0); err != nil { 701 return n, err 702 } 703 case "J": 704 // [J Erases from the cursor to the end of the screen, including the cursor position. 705 // [1J Erases from the beginning of the screen to the cursor, including the cursor position. 706 // [2J Erases the complete display. The cursor does not move. 707 // Clears the screen and moves the cursor to the home position (line 0, column 0). 708 value, err := parseInt16OrDefault(parsedCommand.getParam(0), 0) 709 if err != nil { 710 return n, err 711 } 712 var start COORD 713 var cursor COORD 714 var end COORD 715 screenBufferInfo, err := GetConsoleScreenBufferInfo(handle) 716 if err != nil { 717 return n, err 718 } 719 switch value { 720 case 0: 721 start = screenBufferInfo.CursorPosition 722 // end of the buffer 723 end.X = screenBufferInfo.Size.X - 1 724 end.Y = screenBufferInfo.Size.Y - 1 725 // cursor 726 cursor = screenBufferInfo.CursorPosition 727 case 1: 728 729 // start of the screen 730 start.X = 0 731 start.Y = 0 732 // end of the screen 733 end = screenBufferInfo.CursorPosition 734 // cursor 735 cursor = screenBufferInfo.CursorPosition 736 case 2: 737 // start of the screen 738 start.X = 0 739 start.Y = 0 740 // end of the buffer 741 end.X = screenBufferInfo.Size.X - 1 742 end.Y = screenBufferInfo.Size.Y - 1 743 // cursor 744 cursor.X = 0 745 cursor.Y = 0 746 } 747 if _, err := clearDisplayRange(uintptr(handle), term.screenBufferInfo.Attributes, start, end); err != nil { 748 return n, err 749 } 750 // remember the the cursor position is 1 based 751 if err := setConsoleCursorPosition(handle, false, int16(cursor.X), int16(cursor.Y)); err != nil { 752 return n, err 753 } 754 755 case "K": 756 // [K 757 // Clears all characters from the cursor position to the end of the line (including the character at the cursor position). 758 // [K Erases from the cursor to the end of the line, including the cursor position. 759 // [1K Erases from the beginning of the line to the cursor, including the cursor position. 760 // [2K Erases the complete line. 761 value, err := parseInt16OrDefault(parsedCommand.getParam(0), 0) 762 var start COORD 763 var cursor COORD 764 var end COORD 765 screenBufferInfo, err := GetConsoleScreenBufferInfo(uintptr(handle)) 766 if err != nil { 767 return n, err 768 } 769 switch value { 770 case 0: 771 // start is where cursor is 772 start = screenBufferInfo.CursorPosition 773 // end of line 774 end.X = screenBufferInfo.Size.X - 1 775 end.Y = screenBufferInfo.CursorPosition.Y 776 // cursor remains the same 777 cursor = screenBufferInfo.CursorPosition 778 779 case 1: 780 // beginning of line 781 start.X = 0 782 start.Y = screenBufferInfo.CursorPosition.Y 783 // until cursor 784 end = screenBufferInfo.CursorPosition 785 // cursor remains the same 786 cursor = screenBufferInfo.CursorPosition 787 case 2: 788 // start of the line 789 start.X = 0 790 start.Y = screenBufferInfo.CursorPosition.Y - 1 791 // end of the line 792 end.X = screenBufferInfo.Size.X - 1 793 end.Y = screenBufferInfo.CursorPosition.Y - 1 794 // cursor 795 cursor.X = 0 796 cursor.Y = screenBufferInfo.CursorPosition.Y - 1 797 } 798 if _, err := clearDisplayRange(uintptr(handle), term.screenBufferInfo.Attributes, start, end); err != nil { 799 return n, err 800 } 801 // remember the the cursor position is 1 based 802 if err := setConsoleCursorPosition(uintptr(handle), false, int16(cursor.X), int16(cursor.Y)); err != nil { 803 return n, err 804 } 805 806 case "l": 807 for _, value := range parsedCommand.Parameters { 808 switch value { 809 case "?25", "25": 810 SetCursorVisible(uintptr(handle), BOOL(0)) 811 case "?1049", "1049": 812 // TODO (azlinux): Restore terminal 813 case "?1", "1": 814 // If the DECCKM function is reset, then the arrow keys send ANSI cursor sequences to the host. 815 term.inputEscapeSequence = []byte(KEY_ESC_CSI) 816 } 817 } 818 case "h": 819 for _, value := range parsedCommand.Parameters { 820 switch value { 821 case "?25", "25": 822 SetCursorVisible(uintptr(handle), BOOL(1)) 823 case "?1049", "1049": 824 // TODO (azlinux): Save terminal 825 case "?1", "1": 826 // If the DECCKM function is set, then the arrow keys send application sequences to the host. 827 // DECCKM (default off): When set, the cursor keys send an ESC O prefix, rather than ESC [. 828 term.inputEscapeSequence = []byte(KEY_ESC_O) 829 } 830 } 831 832 case "]": 833 /* 834 TODO (azlinux): 835 Linux Console Private CSI Sequences 836 837 The following sequences are neither ECMA-48 nor native VT102. They are 838 native to the Linux console driver. Colors are in SGR parameters: 0 = 839 black, 1 = red, 2 = green, 3 = brown, 4 = blue, 5 = magenta, 6 = cyan, 840 7 = white. 841 842 ESC [ 1 ; n ] Set color n as the underline color 843 ESC [ 2 ; n ] Set color n as the dim color 844 ESC [ 8 ] Make the current color pair the default attributes. 845 ESC [ 9 ; n ] Set screen blank timeout to n minutes. 846 ESC [ 10 ; n ] Set bell frequency in Hz. 847 ESC [ 11 ; n ] Set bell duration in msec. 848 ESC [ 12 ; n ] Bring specified console to the front. 849 ESC [ 13 ] Unblank the screen. 850 ESC [ 14 ; n ] Set the VESA powerdown interval in minutes. 851 852 */ 853 } 854 return n, nil 855 } 856 857 // WriteChars writes the bytes to given writer. 858 func (term *WindowsTerminal) WriteChars(fd uintptr, w io.Writer, p []byte) (n int, err error) { 859 if len(p) == 0 { 860 return 0, nil 861 } 862 return w.Write(p) 863 } 864 865 const ( 866 CAPSLOCK_ON = 0x0080 //The CAPS LOCK light is on. 867 ENHANCED_KEY = 0x0100 //The key is enhanced. 868 LEFT_ALT_PRESSED = 0x0002 //The left ALT key is pressed. 869 LEFT_CTRL_PRESSED = 0x0008 //The left CTRL key is pressed. 870 NUMLOCK_ON = 0x0020 //The NUM LOCK light is on. 871 RIGHT_ALT_PRESSED = 0x0001 //The right ALT key is pressed. 872 RIGHT_CTRL_PRESSED = 0x0004 //The right CTRL key is pressed. 873 SCROLLLOCK_ON = 0x0040 //The SCROLL LOCK light is on. 874 SHIFT_PRESSED = 0x0010 // The SHIFT key is pressed. 875 ) 876 877 const ( 878 KEY_CONTROL_PARAM_2 = ";2" 879 KEY_CONTROL_PARAM_3 = ";3" 880 KEY_CONTROL_PARAM_4 = ";4" 881 KEY_CONTROL_PARAM_5 = ";5" 882 KEY_CONTROL_PARAM_6 = ";6" 883 KEY_CONTROL_PARAM_7 = ";7" 884 KEY_CONTROL_PARAM_8 = ";8" 885 KEY_ESC_CSI = "\x1B[" 886 KEY_ESC_N = "\x1BN" 887 KEY_ESC_O = "\x1BO" 888 ) 889 890 var keyMapPrefix = map[WORD]string{ 891 VK_UP: "\x1B[%sA", 892 VK_DOWN: "\x1B[%sB", 893 VK_RIGHT: "\x1B[%sC", 894 VK_LEFT: "\x1B[%sD", 895 VK_HOME: "\x1B[1%s~", // showkey shows ^[[1 896 VK_END: "\x1B[4%s~", // showkey shows ^[[4 897 VK_INSERT: "\x1B[2%s~", 898 VK_DELETE: "\x1B[3%s~", 899 VK_PRIOR: "\x1B[5%s~", 900 VK_NEXT: "\x1B[6%s~", 901 VK_F1: "", 902 VK_F2: "", 903 VK_F3: "\x1B[13%s~", 904 VK_F4: "\x1B[14%s~", 905 VK_F5: "\x1B[15%s~", 906 VK_F6: "\x1B[17%s~", 907 VK_F7: "\x1B[18%s~", 908 VK_F8: "\x1B[19%s~", 909 VK_F9: "\x1B[20%s~", 910 VK_F10: "\x1B[21%s~", 911 VK_F11: "\x1B[23%s~", 912 VK_F12: "\x1B[24%s~", 913 } 914 915 var arrowKeyMapPrefix = map[WORD]string{ 916 VK_UP: "%s%sA", 917 VK_DOWN: "%s%sB", 918 VK_RIGHT: "%s%sC", 919 VK_LEFT: "%s%sD", 920 } 921 922 func getControlStateParameter(shift, alt, control, meta bool) string { 923 if shift && alt && control { 924 return KEY_CONTROL_PARAM_8 925 } 926 if alt && control { 927 return KEY_CONTROL_PARAM_7 928 } 929 if shift && control { 930 return KEY_CONTROL_PARAM_6 931 } 932 if control { 933 return KEY_CONTROL_PARAM_5 934 } 935 if shift && alt { 936 return KEY_CONTROL_PARAM_4 937 } 938 if alt { 939 return KEY_CONTROL_PARAM_3 940 } 941 if shift { 942 return KEY_CONTROL_PARAM_2 943 } 944 return "" 945 } 946 947 func getControlKeys(controlState DWORD) (shift, alt, control bool) { 948 shift = 0 != (controlState & SHIFT_PRESSED) 949 alt = 0 != (controlState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) 950 control = 0 != (controlState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) 951 return shift, alt, control 952 } 953 954 func charSequenceForKeys(key WORD, controlState DWORD, escapeSequence []byte) string { 955 i, ok := arrowKeyMapPrefix[key] 956 if ok { 957 shift, alt, control := getControlKeys(controlState) 958 modifier := getControlStateParameter(shift, alt, control, false) 959 return fmt.Sprintf(i, escapeSequence, modifier) 960 } 961 962 i, ok = keyMapPrefix[key] 963 if ok { 964 shift, alt, control := getControlKeys(controlState) 965 modifier := getControlStateParameter(shift, alt, control, false) 966 return fmt.Sprintf(i, modifier) 967 } 968 969 return "" 970 } 971 972 // mapKeystokeToTerminalString maps the given input event record to string 973 func mapKeystokeToTerminalString(keyEvent *KEY_EVENT_RECORD, escapeSequence []byte) string { 974 _, alt, control := getControlKeys(keyEvent.ControlKeyState) 975 if keyEvent.UnicodeChar == 0 { 976 return charSequenceForKeys(keyEvent.VirtualKeyCode, keyEvent.ControlKeyState, escapeSequence) 977 } 978 if control { 979 // TODO(azlinux): Implement following control sequences 980 // <Ctrl>-D Signals the end of input from the keyboard; also exits current shell. 981 // <Ctrl>-H Deletes the first character to the left of the cursor. Also called the ERASE key. 982 // <Ctrl>-Q Restarts printing after it has been stopped with <Ctrl>-s. 983 // <Ctrl>-S Suspends printing on the screen (does not stop the program). 984 // <Ctrl>-U Deletes all characters on the current line. Also called the KILL key. 985 // <Ctrl>-E Quits current command and creates a core 986 987 } 988 // <Alt>+Key generates ESC N Key 989 if !control && alt { 990 return KEY_ESC_N + strings.ToLower(string(keyEvent.UnicodeChar)) 991 } 992 return string(keyEvent.UnicodeChar) 993 } 994 995 // getAvailableInputEvents polls the console for availble events 996 // The function does not return until at least one input record has been read. 997 func getAvailableInputEvents(handle uintptr, inputEvents []INPUT_RECORD) (n int, err error) { 998 // TODO(azlinux): Why is there a for loop? Seems to me, that `n` cannot be negative. - tibor 999 for { 1000 // Read number of console events available 1001 n, err = readConsoleInputKey(handle, inputEvents) 1002 if err != nil || n >= 0 { 1003 return n, err 1004 } 1005 } 1006 } 1007 1008 // getTranslatedKeyCodes converts the input events into the string of characters 1009 // The ansi escape sequence are used to map key strokes to the strings 1010 func getTranslatedKeyCodes(inputEvents []INPUT_RECORD, escapeSequence []byte) string { 1011 var buf bytes.Buffer 1012 for i := 0; i < len(inputEvents); i++ { 1013 input := inputEvents[i] 1014 if input.EventType == KEY_EVENT && input.KeyEvent.KeyDown != 0 { 1015 keyString := mapKeystokeToTerminalString(&input.KeyEvent, escapeSequence) 1016 buf.WriteString(keyString) 1017 } 1018 } 1019 return buf.String() 1020 } 1021 1022 // ReadChars reads the characters from the given reader 1023 func (term *WindowsTerminal) ReadChars(fd uintptr, r io.Reader, p []byte) (n int, err error) { 1024 for term.inputSize == 0 { 1025 nr, err := getAvailableInputEvents(fd, term.inputEvents) 1026 if nr == 0 && nil != err { 1027 return n, err 1028 } 1029 if nr > 0 { 1030 keyCodes := getTranslatedKeyCodes(term.inputEvents[:nr], term.inputEscapeSequence) 1031 term.inputSize = copy(term.inputBuffer, keyCodes) 1032 } 1033 } 1034 n = copy(p, term.inputBuffer[:term.inputSize]) 1035 term.inputSize -= n 1036 return n, nil 1037 } 1038 1039 // HandleInputSequence interprets the input sequence command 1040 func (term *WindowsTerminal) HandleInputSequence(fd uintptr, command []byte) (n int, err error) { 1041 return 0, nil 1042 } 1043 1044 func marshal(c COORD) uintptr { 1045 return uintptr(*((*DWORD)(unsafe.Pointer(&c)))) 1046 } 1047 1048 // IsConsole returns true if the given file descriptor is a terminal. 1049 // -- The code assumes that GetConsoleMode will return an error for file descriptors that are not a console. 1050 func IsConsole(fd uintptr) bool { 1051 _, e := GetConsoleMode(fd) 1052 return e == nil 1053 }