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