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