1// +build windows 2// +build !appengine 3 4package colorable 5 6import ( 7 "bytes" 8 "io" 9 "math" 10 "os" 11 "strconv" 12 "strings" 13 "sync" 14 "syscall" 15 "unsafe" 16 17 "github.com/mattn/go-isatty" 18) 19 20const ( 21 foregroundBlue = 0x1 22 foregroundGreen = 0x2 23 foregroundRed = 0x4 24 foregroundIntensity = 0x8 25 foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity) 26 backgroundBlue = 0x10 27 backgroundGreen = 0x20 28 backgroundRed = 0x40 29 backgroundIntensity = 0x80 30 backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity) 31 commonLvbUnderscore = 0x8000 32 33 cENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4 34) 35 36const ( 37 genericRead = 0x80000000 38 genericWrite = 0x40000000 39) 40 41const ( 42 consoleTextmodeBuffer = 0x1 43) 44 45type wchar uint16 46type short int16 47type dword uint32 48type word uint16 49 50type coord struct { 51 x short 52 y short 53} 54 55type smallRect struct { 56 left short 57 top short 58 right short 59 bottom short 60} 61 62type consoleScreenBufferInfo struct { 63 size coord 64 cursorPosition coord 65 attributes word 66 window smallRect 67 maximumWindowSize coord 68} 69 70type consoleCursorInfo struct { 71 size dword 72 visible int32 73} 74 75var ( 76 kernel32 = syscall.NewLazyDLL("kernel32.dll") 77 procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") 78 procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") 79 procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") 80 procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") 81 procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") 82 procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo") 83 procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo") 84 procSetConsoleTitle = kernel32.NewProc("SetConsoleTitleW") 85 procGetConsoleMode = kernel32.NewProc("GetConsoleMode") 86 procSetConsoleMode = kernel32.NewProc("SetConsoleMode") 87 procCreateConsoleScreenBuffer = kernel32.NewProc("CreateConsoleScreenBuffer") 88) 89 90// Writer provides colorable Writer to the console 91type Writer struct { 92 out io.Writer 93 handle syscall.Handle 94 althandle syscall.Handle 95 oldattr word 96 oldpos coord 97 rest bytes.Buffer 98 mutex sync.Mutex 99} 100 101// NewColorable returns new instance of Writer which handles escape sequence from File. 102func NewColorable(file *os.File) io.Writer { 103 if file == nil { 104 panic("nil passed instead of *os.File to NewColorable()") 105 } 106 107 if isatty.IsTerminal(file.Fd()) { 108 var mode uint32 109 if r, _, _ := procGetConsoleMode.Call(file.Fd(), uintptr(unsafe.Pointer(&mode))); r != 0 && mode&cENABLE_VIRTUAL_TERMINAL_PROCESSING != 0 { 110 return file 111 } 112 var csbi consoleScreenBufferInfo 113 handle := syscall.Handle(file.Fd()) 114 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 115 return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}} 116 } 117 return file 118} 119 120// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. 121func NewColorableStdout() io.Writer { 122 return NewColorable(os.Stdout) 123} 124 125// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. 126func NewColorableStderr() io.Writer { 127 return NewColorable(os.Stderr) 128} 129 130var color256 = map[int]int{ 131 0: 0x000000, 132 1: 0x800000, 133 2: 0x008000, 134 3: 0x808000, 135 4: 0x000080, 136 5: 0x800080, 137 6: 0x008080, 138 7: 0xc0c0c0, 139 8: 0x808080, 140 9: 0xff0000, 141 10: 0x00ff00, 142 11: 0xffff00, 143 12: 0x0000ff, 144 13: 0xff00ff, 145 14: 0x00ffff, 146 15: 0xffffff, 147 16: 0x000000, 148 17: 0x00005f, 149 18: 0x000087, 150 19: 0x0000af, 151 20: 0x0000d7, 152 21: 0x0000ff, 153 22: 0x005f00, 154 23: 0x005f5f, 155 24: 0x005f87, 156 25: 0x005faf, 157 26: 0x005fd7, 158 27: 0x005fff, 159 28: 0x008700, 160 29: 0x00875f, 161 30: 0x008787, 162 31: 0x0087af, 163 32: 0x0087d7, 164 33: 0x0087ff, 165 34: 0x00af00, 166 35: 0x00af5f, 167 36: 0x00af87, 168 37: 0x00afaf, 169 38: 0x00afd7, 170 39: 0x00afff, 171 40: 0x00d700, 172 41: 0x00d75f, 173 42: 0x00d787, 174 43: 0x00d7af, 175 44: 0x00d7d7, 176 45: 0x00d7ff, 177 46: 0x00ff00, 178 47: 0x00ff5f, 179 48: 0x00ff87, 180 49: 0x00ffaf, 181 50: 0x00ffd7, 182 51: 0x00ffff, 183 52: 0x5f0000, 184 53: 0x5f005f, 185 54: 0x5f0087, 186 55: 0x5f00af, 187 56: 0x5f00d7, 188 57: 0x5f00ff, 189 58: 0x5f5f00, 190 59: 0x5f5f5f, 191 60: 0x5f5f87, 192 61: 0x5f5faf, 193 62: 0x5f5fd7, 194 63: 0x5f5fff, 195 64: 0x5f8700, 196 65: 0x5f875f, 197 66: 0x5f8787, 198 67: 0x5f87af, 199 68: 0x5f87d7, 200 69: 0x5f87ff, 201 70: 0x5faf00, 202 71: 0x5faf5f, 203 72: 0x5faf87, 204 73: 0x5fafaf, 205 74: 0x5fafd7, 206 75: 0x5fafff, 207 76: 0x5fd700, 208 77: 0x5fd75f, 209 78: 0x5fd787, 210 79: 0x5fd7af, 211 80: 0x5fd7d7, 212 81: 0x5fd7ff, 213 82: 0x5fff00, 214 83: 0x5fff5f, 215 84: 0x5fff87, 216 85: 0x5fffaf, 217 86: 0x5fffd7, 218 87: 0x5fffff, 219 88: 0x870000, 220 89: 0x87005f, 221 90: 0x870087, 222 91: 0x8700af, 223 92: 0x8700d7, 224 93: 0x8700ff, 225 94: 0x875f00, 226 95: 0x875f5f, 227 96: 0x875f87, 228 97: 0x875faf, 229 98: 0x875fd7, 230 99: 0x875fff, 231 100: 0x878700, 232 101: 0x87875f, 233 102: 0x878787, 234 103: 0x8787af, 235 104: 0x8787d7, 236 105: 0x8787ff, 237 106: 0x87af00, 238 107: 0x87af5f, 239 108: 0x87af87, 240 109: 0x87afaf, 241 110: 0x87afd7, 242 111: 0x87afff, 243 112: 0x87d700, 244 113: 0x87d75f, 245 114: 0x87d787, 246 115: 0x87d7af, 247 116: 0x87d7d7, 248 117: 0x87d7ff, 249 118: 0x87ff00, 250 119: 0x87ff5f, 251 120: 0x87ff87, 252 121: 0x87ffaf, 253 122: 0x87ffd7, 254 123: 0x87ffff, 255 124: 0xaf0000, 256 125: 0xaf005f, 257 126: 0xaf0087, 258 127: 0xaf00af, 259 128: 0xaf00d7, 260 129: 0xaf00ff, 261 130: 0xaf5f00, 262 131: 0xaf5f5f, 263 132: 0xaf5f87, 264 133: 0xaf5faf, 265 134: 0xaf5fd7, 266 135: 0xaf5fff, 267 136: 0xaf8700, 268 137: 0xaf875f, 269 138: 0xaf8787, 270 139: 0xaf87af, 271 140: 0xaf87d7, 272 141: 0xaf87ff, 273 142: 0xafaf00, 274 143: 0xafaf5f, 275 144: 0xafaf87, 276 145: 0xafafaf, 277 146: 0xafafd7, 278 147: 0xafafff, 279 148: 0xafd700, 280 149: 0xafd75f, 281 150: 0xafd787, 282 151: 0xafd7af, 283 152: 0xafd7d7, 284 153: 0xafd7ff, 285 154: 0xafff00, 286 155: 0xafff5f, 287 156: 0xafff87, 288 157: 0xafffaf, 289 158: 0xafffd7, 290 159: 0xafffff, 291 160: 0xd70000, 292 161: 0xd7005f, 293 162: 0xd70087, 294 163: 0xd700af, 295 164: 0xd700d7, 296 165: 0xd700ff, 297 166: 0xd75f00, 298 167: 0xd75f5f, 299 168: 0xd75f87, 300 169: 0xd75faf, 301 170: 0xd75fd7, 302 171: 0xd75fff, 303 172: 0xd78700, 304 173: 0xd7875f, 305 174: 0xd78787, 306 175: 0xd787af, 307 176: 0xd787d7, 308 177: 0xd787ff, 309 178: 0xd7af00, 310 179: 0xd7af5f, 311 180: 0xd7af87, 312 181: 0xd7afaf, 313 182: 0xd7afd7, 314 183: 0xd7afff, 315 184: 0xd7d700, 316 185: 0xd7d75f, 317 186: 0xd7d787, 318 187: 0xd7d7af, 319 188: 0xd7d7d7, 320 189: 0xd7d7ff, 321 190: 0xd7ff00, 322 191: 0xd7ff5f, 323 192: 0xd7ff87, 324 193: 0xd7ffaf, 325 194: 0xd7ffd7, 326 195: 0xd7ffff, 327 196: 0xff0000, 328 197: 0xff005f, 329 198: 0xff0087, 330 199: 0xff00af, 331 200: 0xff00d7, 332 201: 0xff00ff, 333 202: 0xff5f00, 334 203: 0xff5f5f, 335 204: 0xff5f87, 336 205: 0xff5faf, 337 206: 0xff5fd7, 338 207: 0xff5fff, 339 208: 0xff8700, 340 209: 0xff875f, 341 210: 0xff8787, 342 211: 0xff87af, 343 212: 0xff87d7, 344 213: 0xff87ff, 345 214: 0xffaf00, 346 215: 0xffaf5f, 347 216: 0xffaf87, 348 217: 0xffafaf, 349 218: 0xffafd7, 350 219: 0xffafff, 351 220: 0xffd700, 352 221: 0xffd75f, 353 222: 0xffd787, 354 223: 0xffd7af, 355 224: 0xffd7d7, 356 225: 0xffd7ff, 357 226: 0xffff00, 358 227: 0xffff5f, 359 228: 0xffff87, 360 229: 0xffffaf, 361 230: 0xffffd7, 362 231: 0xffffff, 363 232: 0x080808, 364 233: 0x121212, 365 234: 0x1c1c1c, 366 235: 0x262626, 367 236: 0x303030, 368 237: 0x3a3a3a, 369 238: 0x444444, 370 239: 0x4e4e4e, 371 240: 0x585858, 372 241: 0x626262, 373 242: 0x6c6c6c, 374 243: 0x767676, 375 244: 0x808080, 376 245: 0x8a8a8a, 377 246: 0x949494, 378 247: 0x9e9e9e, 379 248: 0xa8a8a8, 380 249: 0xb2b2b2, 381 250: 0xbcbcbc, 382 251: 0xc6c6c6, 383 252: 0xd0d0d0, 384 253: 0xdadada, 385 254: 0xe4e4e4, 386 255: 0xeeeeee, 387} 388 389// `\033]0;TITLESTR\007` 390func doTitleSequence(er *bytes.Reader) error { 391 var c byte 392 var err error 393 394 c, err = er.ReadByte() 395 if err != nil { 396 return err 397 } 398 if c != '0' && c != '2' { 399 return nil 400 } 401 c, err = er.ReadByte() 402 if err != nil { 403 return err 404 } 405 if c != ';' { 406 return nil 407 } 408 title := make([]byte, 0, 80) 409 for { 410 c, err = er.ReadByte() 411 if err != nil { 412 return err 413 } 414 if c == 0x07 || c == '\n' { 415 break 416 } 417 title = append(title, c) 418 } 419 if len(title) > 0 { 420 title8, err := syscall.UTF16PtrFromString(string(title)) 421 if err == nil { 422 procSetConsoleTitle.Call(uintptr(unsafe.Pointer(title8))) 423 } 424 } 425 return nil 426} 427 428// returns Atoi(s) unless s == "" in which case it returns def 429func atoiWithDefault(s string, def int) (int, error) { 430 if s == "" { 431 return def, nil 432 } 433 return strconv.Atoi(s) 434} 435 436// Write writes data on console 437func (w *Writer) Write(data []byte) (n int, err error) { 438 w.mutex.Lock() 439 defer w.mutex.Unlock() 440 var csbi consoleScreenBufferInfo 441 procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) 442 443 handle := w.handle 444 445 var er *bytes.Reader 446 if w.rest.Len() > 0 { 447 var rest bytes.Buffer 448 w.rest.WriteTo(&rest) 449 w.rest.Reset() 450 rest.Write(data) 451 er = bytes.NewReader(rest.Bytes()) 452 } else { 453 er = bytes.NewReader(data) 454 } 455 var bw [1]byte 456loop: 457 for { 458 c1, err := er.ReadByte() 459 if err != nil { 460 break loop 461 } 462 if c1 != 0x1b { 463 bw[0] = c1 464 w.out.Write(bw[:]) 465 continue 466 } 467 c2, err := er.ReadByte() 468 if err != nil { 469 break loop 470 } 471 472 switch c2 { 473 case '>': 474 continue 475 case ']': 476 w.rest.WriteByte(c1) 477 w.rest.WriteByte(c2) 478 er.WriteTo(&w.rest) 479 if bytes.IndexByte(w.rest.Bytes(), 0x07) == -1 { 480 break loop 481 } 482 er = bytes.NewReader(w.rest.Bytes()[2:]) 483 err := doTitleSequence(er) 484 if err != nil { 485 break loop 486 } 487 w.rest.Reset() 488 continue 489 // https://github.com/mattn/go-colorable/issues/27 490 case '7': 491 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 492 w.oldpos = csbi.cursorPosition 493 continue 494 case '8': 495 procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) 496 continue 497 case 0x5b: 498 // execute part after switch 499 default: 500 continue 501 } 502 503 w.rest.WriteByte(c1) 504 w.rest.WriteByte(c2) 505 er.WriteTo(&w.rest) 506 507 var buf bytes.Buffer 508 var m byte 509 for i, c := range w.rest.Bytes()[2:] { 510 if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { 511 m = c 512 er = bytes.NewReader(w.rest.Bytes()[2+i+1:]) 513 w.rest.Reset() 514 break 515 } 516 buf.Write([]byte(string(c))) 517 } 518 if m == 0 { 519 break loop 520 } 521 522 switch m { 523 case 'A': 524 n, err = atoiWithDefault(buf.String(), 1) 525 if err != nil { 526 continue 527 } 528 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 529 csbi.cursorPosition.y -= short(n) 530 procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 531 case 'B': 532 n, err = atoiWithDefault(buf.String(), 1) 533 if err != nil { 534 continue 535 } 536 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 537 csbi.cursorPosition.y += short(n) 538 procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 539 case 'C': 540 n, err = atoiWithDefault(buf.String(), 1) 541 if err != nil { 542 continue 543 } 544 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 545 csbi.cursorPosition.x += short(n) 546 procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 547 case 'D': 548 n, err = atoiWithDefault(buf.String(), 1) 549 if err != nil { 550 continue 551 } 552 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 553 csbi.cursorPosition.x -= short(n) 554 if csbi.cursorPosition.x < 0 { 555 csbi.cursorPosition.x = 0 556 } 557 procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 558 case 'E': 559 n, err = strconv.Atoi(buf.String()) 560 if err != nil { 561 continue 562 } 563 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 564 csbi.cursorPosition.x = 0 565 csbi.cursorPosition.y += short(n) 566 procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 567 case 'F': 568 n, err = strconv.Atoi(buf.String()) 569 if err != nil { 570 continue 571 } 572 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 573 csbi.cursorPosition.x = 0 574 csbi.cursorPosition.y -= short(n) 575 procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 576 case 'G': 577 n, err = strconv.Atoi(buf.String()) 578 if err != nil { 579 continue 580 } 581 if n < 1 { 582 n = 1 583 } 584 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 585 csbi.cursorPosition.x = short(n - 1) 586 procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 587 case 'H', 'f': 588 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 589 if buf.Len() > 0 { 590 token := strings.Split(buf.String(), ";") 591 switch len(token) { 592 case 1: 593 n1, err := strconv.Atoi(token[0]) 594 if err != nil { 595 continue 596 } 597 csbi.cursorPosition.y = short(n1 - 1) 598 case 2: 599 n1, err := strconv.Atoi(token[0]) 600 if err != nil { 601 continue 602 } 603 n2, err := strconv.Atoi(token[1]) 604 if err != nil { 605 continue 606 } 607 csbi.cursorPosition.x = short(n2 - 1) 608 csbi.cursorPosition.y = short(n1 - 1) 609 } 610 } else { 611 csbi.cursorPosition.y = 0 612 } 613 procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 614 case 'J': 615 n := 0 616 if buf.Len() > 0 { 617 n, err = strconv.Atoi(buf.String()) 618 if err != nil { 619 continue 620 } 621 } 622 var count, written dword 623 var cursor coord 624 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 625 switch n { 626 case 0: 627 cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} 628 count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x) 629 case 1: 630 cursor = coord{x: csbi.window.left, y: csbi.window.top} 631 count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.window.top-csbi.cursorPosition.y)*dword(csbi.size.x) 632 case 2: 633 cursor = coord{x: csbi.window.left, y: csbi.window.top} 634 count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x) 635 } 636 procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) 637 procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) 638 case 'K': 639 n := 0 640 if buf.Len() > 0 { 641 n, err = strconv.Atoi(buf.String()) 642 if err != nil { 643 continue 644 } 645 } 646 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 647 var cursor coord 648 var count, written dword 649 switch n { 650 case 0: 651 cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} 652 count = dword(csbi.size.x - csbi.cursorPosition.x) 653 case 1: 654 cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y} 655 count = dword(csbi.size.x - csbi.cursorPosition.x) 656 case 2: 657 cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y} 658 count = dword(csbi.size.x) 659 } 660 procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) 661 procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) 662 case 'X': 663 n := 0 664 if buf.Len() > 0 { 665 n, err = strconv.Atoi(buf.String()) 666 if err != nil { 667 continue 668 } 669 } 670 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 671 var cursor coord 672 var written dword 673 cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} 674 procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) 675 procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) 676 case 'm': 677 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 678 attr := csbi.attributes 679 cs := buf.String() 680 if cs == "" { 681 procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(w.oldattr)) 682 continue 683 } 684 token := strings.Split(cs, ";") 685 for i := 0; i < len(token); i++ { 686 ns := token[i] 687 if n, err = strconv.Atoi(ns); err == nil { 688 switch { 689 case n == 0 || n == 100: 690 attr = w.oldattr 691 case n == 4: 692 attr |= commonLvbUnderscore 693 case (1 <= n && n <= 3) || n == 5: 694 attr |= foregroundIntensity 695 case n == 7 || n == 27: 696 attr = 697 (attr &^ (foregroundMask | backgroundMask)) | 698 ((attr & foregroundMask) << 4) | 699 ((attr & backgroundMask) >> 4) 700 case n == 22: 701 attr &^= foregroundIntensity 702 case n == 24: 703 attr &^= commonLvbUnderscore 704 case 30 <= n && n <= 37: 705 attr &= backgroundMask 706 if (n-30)&1 != 0 { 707 attr |= foregroundRed 708 } 709 if (n-30)&2 != 0 { 710 attr |= foregroundGreen 711 } 712 if (n-30)&4 != 0 { 713 attr |= foregroundBlue 714 } 715 case n == 38: // set foreground color. 716 if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") { 717 if n256, err := strconv.Atoi(token[i+2]); err == nil { 718 if n256foreAttr == nil { 719 n256setup() 720 } 721 attr &= backgroundMask 722 attr |= n256foreAttr[n256%len(n256foreAttr)] 723 i += 2 724 } 725 } else if len(token) == 5 && token[i+1] == "2" { 726 var r, g, b int 727 r, _ = strconv.Atoi(token[i+2]) 728 g, _ = strconv.Atoi(token[i+3]) 729 b, _ = strconv.Atoi(token[i+4]) 730 i += 4 731 if r > 127 { 732 attr |= foregroundRed 733 } 734 if g > 127 { 735 attr |= foregroundGreen 736 } 737 if b > 127 { 738 attr |= foregroundBlue 739 } 740 } else { 741 attr = attr & (w.oldattr & backgroundMask) 742 } 743 case n == 39: // reset foreground color. 744 attr &= backgroundMask 745 attr |= w.oldattr & foregroundMask 746 case 40 <= n && n <= 47: 747 attr &= foregroundMask 748 if (n-40)&1 != 0 { 749 attr |= backgroundRed 750 } 751 if (n-40)&2 != 0 { 752 attr |= backgroundGreen 753 } 754 if (n-40)&4 != 0 { 755 attr |= backgroundBlue 756 } 757 case n == 48: // set background color. 758 if i < len(token)-2 && token[i+1] == "5" { 759 if n256, err := strconv.Atoi(token[i+2]); err == nil { 760 if n256backAttr == nil { 761 n256setup() 762 } 763 attr &= foregroundMask 764 attr |= n256backAttr[n256%len(n256backAttr)] 765 i += 2 766 } 767 } else if len(token) == 5 && token[i+1] == "2" { 768 var r, g, b int 769 r, _ = strconv.Atoi(token[i+2]) 770 g, _ = strconv.Atoi(token[i+3]) 771 b, _ = strconv.Atoi(token[i+4]) 772 i += 4 773 if r > 127 { 774 attr |= backgroundRed 775 } 776 if g > 127 { 777 attr |= backgroundGreen 778 } 779 if b > 127 { 780 attr |= backgroundBlue 781 } 782 } else { 783 attr = attr & (w.oldattr & foregroundMask) 784 } 785 case n == 49: // reset foreground color. 786 attr &= foregroundMask 787 attr |= w.oldattr & backgroundMask 788 case 90 <= n && n <= 97: 789 attr = (attr & backgroundMask) 790 attr |= foregroundIntensity 791 if (n-90)&1 != 0 { 792 attr |= foregroundRed 793 } 794 if (n-90)&2 != 0 { 795 attr |= foregroundGreen 796 } 797 if (n-90)&4 != 0 { 798 attr |= foregroundBlue 799 } 800 case 100 <= n && n <= 107: 801 attr = (attr & foregroundMask) 802 attr |= backgroundIntensity 803 if (n-100)&1 != 0 { 804 attr |= backgroundRed 805 } 806 if (n-100)&2 != 0 { 807 attr |= backgroundGreen 808 } 809 if (n-100)&4 != 0 { 810 attr |= backgroundBlue 811 } 812 } 813 procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr)) 814 } 815 } 816 case 'h': 817 var ci consoleCursorInfo 818 cs := buf.String() 819 if cs == "5>" { 820 procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) 821 ci.visible = 0 822 procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) 823 } else if cs == "?25" { 824 procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) 825 ci.visible = 1 826 procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) 827 } else if cs == "?1049" { 828 if w.althandle == 0 { 829 h, _, _ := procCreateConsoleScreenBuffer.Call(uintptr(genericRead|genericWrite), 0, 0, uintptr(consoleTextmodeBuffer), 0, 0) 830 w.althandle = syscall.Handle(h) 831 if w.althandle != 0 { 832 handle = w.althandle 833 } 834 } 835 } 836 case 'l': 837 var ci consoleCursorInfo 838 cs := buf.String() 839 if cs == "5>" { 840 procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) 841 ci.visible = 1 842 procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) 843 } else if cs == "?25" { 844 procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) 845 ci.visible = 0 846 procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) 847 } else if cs == "?1049" { 848 if w.althandle != 0 { 849 syscall.CloseHandle(w.althandle) 850 w.althandle = 0 851 handle = w.handle 852 } 853 } 854 case 's': 855 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 856 w.oldpos = csbi.cursorPosition 857 case 'u': 858 procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) 859 } 860 } 861 862 return len(data), nil 863} 864 865type consoleColor struct { 866 rgb int 867 red bool 868 green bool 869 blue bool 870 intensity bool 871} 872 873func (c consoleColor) foregroundAttr() (attr word) { 874 if c.red { 875 attr |= foregroundRed 876 } 877 if c.green { 878 attr |= foregroundGreen 879 } 880 if c.blue { 881 attr |= foregroundBlue 882 } 883 if c.intensity { 884 attr |= foregroundIntensity 885 } 886 return 887} 888 889func (c consoleColor) backgroundAttr() (attr word) { 890 if c.red { 891 attr |= backgroundRed 892 } 893 if c.green { 894 attr |= backgroundGreen 895 } 896 if c.blue { 897 attr |= backgroundBlue 898 } 899 if c.intensity { 900 attr |= backgroundIntensity 901 } 902 return 903} 904 905var color16 = []consoleColor{ 906 {0x000000, false, false, false, false}, 907 {0x000080, false, false, true, false}, 908 {0x008000, false, true, false, false}, 909 {0x008080, false, true, true, false}, 910 {0x800000, true, false, false, false}, 911 {0x800080, true, false, true, false}, 912 {0x808000, true, true, false, false}, 913 {0xc0c0c0, true, true, true, false}, 914 {0x808080, false, false, false, true}, 915 {0x0000ff, false, false, true, true}, 916 {0x00ff00, false, true, false, true}, 917 {0x00ffff, false, true, true, true}, 918 {0xff0000, true, false, false, true}, 919 {0xff00ff, true, false, true, true}, 920 {0xffff00, true, true, false, true}, 921 {0xffffff, true, true, true, true}, 922} 923 924type hsv struct { 925 h, s, v float32 926} 927 928func (a hsv) dist(b hsv) float32 { 929 dh := a.h - b.h 930 switch { 931 case dh > 0.5: 932 dh = 1 - dh 933 case dh < -0.5: 934 dh = -1 - dh 935 } 936 ds := a.s - b.s 937 dv := a.v - b.v 938 return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv))) 939} 940 941func toHSV(rgb int) hsv { 942 r, g, b := float32((rgb&0xFF0000)>>16)/256.0, 943 float32((rgb&0x00FF00)>>8)/256.0, 944 float32(rgb&0x0000FF)/256.0 945 min, max := minmax3f(r, g, b) 946 h := max - min 947 if h > 0 { 948 if max == r { 949 h = (g - b) / h 950 if h < 0 { 951 h += 6 952 } 953 } else if max == g { 954 h = 2 + (b-r)/h 955 } else { 956 h = 4 + (r-g)/h 957 } 958 } 959 h /= 6.0 960 s := max - min 961 if max != 0 { 962 s /= max 963 } 964 v := max 965 return hsv{h: h, s: s, v: v} 966} 967 968type hsvTable []hsv 969 970func toHSVTable(rgbTable []consoleColor) hsvTable { 971 t := make(hsvTable, len(rgbTable)) 972 for i, c := range rgbTable { 973 t[i] = toHSV(c.rgb) 974 } 975 return t 976} 977 978func (t hsvTable) find(rgb int) consoleColor { 979 hsv := toHSV(rgb) 980 n := 7 981 l := float32(5.0) 982 for i, p := range t { 983 d := hsv.dist(p) 984 if d < l { 985 l, n = d, i 986 } 987 } 988 return color16[n] 989} 990 991func minmax3f(a, b, c float32) (min, max float32) { 992 if a < b { 993 if b < c { 994 return a, c 995 } else if a < c { 996 return a, b 997 } else { 998 return c, b 999 } 1000 } else { 1001 if a < c { 1002 return b, c 1003 } else if b < c { 1004 return b, a 1005 } else { 1006 return c, a 1007 } 1008 } 1009} 1010 1011var n256foreAttr []word 1012var n256backAttr []word 1013 1014func n256setup() { 1015 n256foreAttr = make([]word, 256) 1016 n256backAttr = make([]word, 256) 1017 t := toHSVTable(color16) 1018 for i, rgb := range color256 { 1019 c := t.find(rgb) 1020 n256foreAttr[i] = c.foregroundAttr() 1021 n256backAttr[i] = c.backgroundAttr() 1022 } 1023} 1024 1025// EnableColorsStdout enable colors if possible. 1026func EnableColorsStdout(enabled *bool) func() { 1027 var mode uint32 1028 h := os.Stdout.Fd() 1029 if r, _, _ := procGetConsoleMode.Call(h, uintptr(unsafe.Pointer(&mode))); r != 0 { 1030 if r, _, _ = procSetConsoleMode.Call(h, uintptr(mode|cENABLE_VIRTUAL_TERMINAL_PROCESSING)); r != 0 { 1031 if enabled != nil { 1032 *enabled = true 1033 } 1034 return func() { 1035 procSetConsoleMode.Call(h, uintptr(mode)) 1036 } 1037 } 1038 } 1039 if enabled != nil { 1040 *enabled = true 1041 } 1042 return func() {} 1043} 1044