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